This commit is contained in:
K.Makise 2026-01-22 14:29:51 +08:00 committed by GitHub
commit 2189af06b0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 1978 additions and 89 deletions

View File

@ -5,12 +5,15 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
# This version aims to keep the track out/vertical to the dff pins
# Now only consider the channel at the south, but works fine with channel at the north
import collections
from openram import debug
from openram.tech import drc
from .vector import vector
from .design import design
from openram import OPTS
import re
class channel_net():
def __init__(self, net_name, pins, vertical):
@ -85,7 +88,8 @@ class channel_route(design):
layer_stack,
directions=None,
vertical=False,
parent=None):
parent=None,
dff_area=False):
"""
The net list is a list of the nets with each net being a list of pins
to be connected. The offset is the lower-left of where the
@ -106,6 +110,7 @@ class channel_route(design):
self.vertical = vertical
# For debugging...
self.parent = parent
self.dff_area = dff_area # this is a special value to handle dff areas, should be true when routing col_dff/dffs
if not directions or directions == "pref":
# Use the preferred layer directions
@ -139,6 +144,16 @@ class channel_route(design):
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
# For debug
debug.warning("layer horizontal: {0}".format(self.horizontal_layer))
debug.warning("horizontal_nonpref_pitch: {0}".format(self.horizontal_nonpref_pitch))
debug.warning("horizontal_pitch: {0}".format(self.horizontal_pitch))
debug.warning("horizontal_space: {0}".format(self.horizontal_space))
debug.warning("layer vertical: {0}".format(self.vertical_layer))
debug.warning("vertiacl_nonpref_pitch: {0}".format(self.vertical_pitch))
debug.warning("vertical_pitch: {0}".format(self.vertical_pitch))
debug.warning("vertical_space: {0}".format(self.vertical_space))
self.route()
@ -220,7 +235,9 @@ class channel_route(design):
else:
real_channel_offset = vector(min(self.min_value, self.offset.x), self.offset.y)
current_offset = real_channel_offset
if self.dff_area == True:
if self.layer_stack == self.m2_stack:
self.vertical_nonpref_pitch = self.horizontal_pitch + 0.1 # 0.1 make sure even if via at same col, fulfill m3-m3 spacing
# Sort nets by left edge value
nets.sort()
while len(nets) > 0:
@ -245,9 +262,14 @@ class channel_route(design):
self.horizontal_pitch)
current_offset = vector(current_offset.x, net.max_value + self.horizontal_nonpref_pitch)
else:
self.add_horizontal_trunk_route(net.pins,
current_offset,
self.vertical_pitch)
if self.dff_area == True: # only use in dff channel routing
self.add_horizontal_trunk_with_jog(net.pins,
current_offset,
self.vertical_pitch)
else:
self.add_horizontal_trunk_route(net.pins,
current_offset,
self.vertical_pitch)
current_offset = vector(net.max_value + self.vertical_nonpref_pitch, current_offset.y)
# Remove the net from other constriants in the VCG
@ -297,6 +319,265 @@ class channel_route(design):
debug.error("Cannot find layer pitch.", -1)
return (nonpref_pitch, pitch, pitch - space, space)
def check_need_jog(self, pin_name):
match = re.search(r'^din\d+_(\d+)$', pin_name)
if match:
number = int(match.group(1))
if number % int(OPTS.write_size) == 0:
return True
else:
return False
else:
return False
def add_horizontal_trunk_with_jog(self,
pins,
trunk_offset,
pitch):
""" Special for connecting channel of dffs & bank, avoid too close with vdd pins """
max_x = max([pin.center().x for pin in pins])
min_x = min([pin.center().x for pin in pins])
min_y = min([pin.center().y for pin in pins])
max_y = max([pin.center().y for pin in pins])
# see the channel is at top or bottom
if min_y < 0: # port0
for pin in pins:
if pin.center().x == max_x:
if pin.center().y == max_y:
min_x = min_x - 0.1 # in order to add jog at the dff pins, avoid overlap with vdd pins, left shift vertical line at dout pin
else: # pin has max_x is under track
max_x = max_x - 0.1
port = 0
else: # port1
for pin in pins:
if pin.center().x == max_x:
if pin.center().y == max_y:
max_x = max_x - 0.1 # in order to add jog at the dff pins, avoid overlap with vdd pins, left shift vertical line at dout pin
else: # pin has max_x is under track
min_x = min_x - 0.1
port = 1
# if we are less than a pitch, just create a non-preferred layer jog
non_preferred_route = max_x - min_x <= pitch
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
if port == 0: # bottom need shift
if non_preferred_route:
# Add the horizontal trunk on the vertical layer!
self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y),
vector(max_x + half_layer_width, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
if pin.cy() < trunk_offset.y:
pin_pos = pin.center()
mid = vector(pin_pos.x - 0.1, trunk_offset.y)
self.add_path(self.vertical_layer, [vector(pin.bc().x - 0.1, pin.bc().y), mid])
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin_pos)
else:
if self.check_need_jog(pin.name) and (OPTS.write_size != OPTS.word_size):# for port0, only pin above track needs to check
# right first
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.rc())
point_1 = vector(pin.center().x + pitch, pin.center().y)
self.add_path(self.vertical_layer, [pin.rc(), point_1])
# straight out of bank area
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_1)
point_2 = vector(point_1.x, point_1.y - 5.6)
self.add_path(self.vertical_layer, [point_1, point_2])
# then left
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_2)
point_3 = vector(pin.center().x, pin.center().y - 5.6)
self.add_path(self.vertical_layer, [point_2, point_3])
# back to normal
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_3)
mid = vector(point_3.x, trunk_offset.y)
self.add_path(self.vertical_layer, [point_3, mid])
else: # do not need to care of wmask
pin_pos = pin.bc()
# No bend needed here
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.bc())
else:
# Add the horizontal trunk
self.add_path(self.horizontal_layer,
[vector(min_x, trunk_offset.y),
vector(max_x, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
debug.warning("pin name in net ----> {0}".format(pin.name))
debug.warning("wmask or not --> {0}".format(OPTS.write_size))
# Find the correct side of the pin
if pin.cy() < trunk_offset.y:
pin_pos = pin.center()
mid = vector(pin_pos.x - 0.1, trunk_offset.y)
self.add_path(self.vertical_layer, [vector(pin.bc().x - 0.1, pin.bc().y), mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin_pos)
else:
if self.check_need_jog(pin.name) and (OPTS.write_size != OPTS.word_size):# for port0, only pin above track needs to check
# right first
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.rc())
point_1 = vector(pin.center().x + pitch, pin.center().y)
self.add_path(self.vertical_layer, [pin.rc(), point_1])
# straight out of bank area
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_1)
point_2 = vector(point_1.x, point_1.y - 5.6)
self.add_path(self.vertical_layer, [point_1, point_2])
# then left
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_2)
point_3 = vector(pin.center().x, pin.center().y - 5.6)
self.add_path(self.vertical_layer, [point_2, point_3])
# back to normal
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_3)
mid = vector(point_3.x, trunk_offset.y)
self.add_path(self.vertical_layer, [point_3, mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
else: # do not need to care of wmask
pin_pos = pin.bc()
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.bc())
else: # port 1, situation different, top need shift
if non_preferred_route:
# Add the horizontal trunk on the vertical layer!
self.add_path(self.vertical_layer,
[vector(min_x - half_layer_width, trunk_offset.y),
vector(max_x + half_layer_width, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
if pin.cy() < trunk_offset.y:
if self.check_need_jog(pin.name) and (OPTS.write_size != OPTS.word_size):# for port0, only pin above track needs to check
# right first
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.rc())
point_1 = vector(pin.center().x + pitch, pin.center().y)
self.add_path(self.vertical_layer, [pin.rc(), point_1])
# straight out of bank area
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_1)
point_2 = vector(point_1.x, point_1.y + 5.6)
self.add_path(self.vertical_layer, [point_1, point_2])
# then left
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_2)
point_3 = vector(pin.center().x, pin.center().y + 5.6)
self.add_path(self.vertical_layer, [point_2, point_3])
# back to normal
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_3)
mid = vector(point_3.x, trunk_offset.y)
self.add_path(self.vertical_layer, [point_3, mid])
else: # do not need to care of wmask
pin_pos = pin.uc()
# No bend needed here
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.uc())
else:
pin_pos = pin.center()
mid = vector(pin_pos.x - 0.1, trunk_offset.y)
self.add_path(self.vertical_layer, [vector(pin.uc().x - 0.1, pin.uc().y), mid])
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin_pos)
else:
# Add the horizontal trunk
self.add_path(self.horizontal_layer,
[vector(min_x, trunk_offset.y),
vector(max_x, trunk_offset.y)])
# Route each pin to the trunk
for pin in pins:
# Find the correct side of the pin
if pin.cy() < trunk_offset.y:
if self.check_need_jog(pin.name) and (OPTS.write_size != OPTS.word_size):# for port0, only pin above track needs to check
# right first
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.rc())
point_1 = vector(pin.center().x + pitch, pin.center().y)
self.add_path(self.vertical_layer, [pin.rc(), point_1])
# straight out of bank area
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_1)
point_2 = vector(point_1.x, point_1.y + 5.6)
self.add_path(self.vertical_layer, [point_1, point_2])
# then left
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_2)
point_3 = vector(pin.center().x, pin.center().y + 5.6)
self.add_path(self.vertical_layer, [point_2, point_3])
# back to normal
self.add_via_stack_center(from_layer=self.vertical_layer,
to_layer=self.vertical_layer,
offset=point_3)
mid = vector(point_3.x, trunk_offset.y)
self.add_path(self.vertical_layer, [point_3, mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
else: # do not need to care of wmask
pin_pos = pin.uc()
mid = vector(pin_pos.x, trunk_offset.y)
self.add_path(self.vertical_layer, [pin_pos, mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin.uc())
else:
pin_pos = pin.center()
mid = vector(pin_pos.x - 0.1, trunk_offset.y)
self.add_path(self.vertical_layer, [vector(pin.uc().x - 0.1, pin.uc().y), mid])
self.add_via_center(layers=self.layer_stack,
offset=mid,
directions=self.directions)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.vertical_layer,
offset=pin_pos)
def add_horizontal_trunk_route(self,
pins,
trunk_offset,

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_m2(self, name, loc, directions=None, start_layer="m1"):
# same function like normal one, but add power pin at m2
# 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 == "m2":
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="m2",
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="m2",
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, route_option="classic"):
""" 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)])
@ -2063,9 +2108,9 @@ class layout():
tap_spacing = 2
nwell_offset = vector(self.nwell_width, self.nwell_width)
# Every nth tap is connected to gnd
# Every nth tap is connected to vdd
period = 5
moat_pins = []
# BOTTOM
count = 0
loc = ll + nwell_offset.scale(tap_spacing, 0)
@ -2080,9 +2125,10 @@ class layout():
to_layer="m1",
offset=loc)
else:
self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
pin = self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
moat_pins.append(pin)
count += 1
loc += nwell_offset.scale(tap_spacing, 0)
@ -2100,9 +2146,10 @@ class layout():
to_layer="m1",
offset=loc)
else:
self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
pin = self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
moat_pins.append(pin)
count += 1
loc += nwell_offset.scale(tap_spacing, 0)
@ -2120,9 +2167,15 @@ class layout():
to_layer="m2",
offset=loc)
else:
self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
if route_option == "classic":
pin = self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
elif route_option == "quality":
pin = self.add_power_pin_m2(name="vdd",
loc=loc,
start_layer="li")
moat_pins.append(pin)
count += 1
loc += nwell_offset.scale(0, tap_spacing)
@ -2140,14 +2193,21 @@ class layout():
to_layer="m2",
offset=loc)
else:
self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
if route_option == "classic":
pin = self.add_power_pin(name="vdd",
loc=loc,
start_layer="li")
elif route_option == "quality":
pin = self.add_power_pin_m2(name="vdd",
loc=loc,
start_layer="li")
moat_pins.append(pin)
count += 1
loc += nwell_offset.scale(0, tap_spacing)
# Add the gnd ring
# Add the vdd ring
self.add_ring([ll, ur])
return moat_pins
def add_ring(self, bbox=None, width_mult=8, offset=0):
"""

View File

@ -742,6 +742,9 @@ class VlsiLayout:
Search for a pin label and return ALL the enclosing rectangles on the same layer
as the pin label.
"""
#debug
for pin in self.pins:
print(pin)
shape_list = []
pin_map = self.pins[pin_name]
for pin_list in pin_map:

View File

@ -5,6 +5,7 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
# This aim to change the position of the submodule
import datetime
from math import ceil
from importlib import import_module, reload
@ -17,6 +18,7 @@ from openram.base import lef
from openram.sram_factory import factory
from openram.tech import spice
from openram import OPTS, print_time
import re
class sram_1bank(design, verilog, lef):
@ -51,6 +53,11 @@ 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 = []
# vdd pins on moat, list of pins
self.moat_pins = []
def add_pins(self):
""" Add pins for entire SRAM. """
@ -206,15 +213,15 @@ class sram_1bank(design, verilog, lef):
if not OPTS.is_unit_test:
print_time("Submodules", datetime.datetime.now(), start_time)
def create_layout(self):
def create_layout(self, position_add=0, mod=0, route_option="classic"):
""" Layout creation """
start_time = datetime.datetime.now()
self.place_instances()
self.place_instances_changeable(position_add=position_add)
if not OPTS.is_unit_test:
print_time("Placement", datetime.datetime.now(), start_time)
start_time = datetime.datetime.now()
self.route_layout()
self.route_layout(mod=mod, route_option=route_option)
if not OPTS.is_unit_test:
print_time("Routing", datetime.datetime.now(), start_time)
@ -242,6 +249,38 @@ 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):
""" Corresponding supply router for io_pin_placer """
# 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,
moat_pins=self.moat_pins)
# add power rings / side pins
if OPTS.supply_pin_type in ["top", "bottom", "right", "left"]:
rtr.add_side_pin("vdd")
rtr.add_side_pin("gnd")# noraml gnd name
elif OPTS.supply_pin_type == "ring":
rtr.add_ring_pin("vdd")
rtr.add_ring_pin("gnd")# normal gnd name
else:
debug.warning("Side supply pins aren't created.")
# Prepare the inside power pins (all power pins of submodules), at m3
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
self.copy_power_pins(inst, pin_name)
# Route all the power pins
#rtr.route_inside() # only connecting the power pins of inside submodules with each other, moat pins will connect to the ring
rtr.route_outside(io_pin_names=self.pins_to_route)
# route moat vdds
#rtr.route_moat(self.pins_to_route)
def route_supplies(self, bbox=None):
""" Route the supply grid and connect the pins to them. """
@ -318,54 +357,68 @@ class sram_1bank(design, verilog, lef):
# Grid is left with many top level pins
pass
def route_escape_pins(self, bbox=None):
def route_escape_pins(self, bbox=None, mod=0, route_option="classic"):
"""
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))
from openram.router import signal_escape_router as router
rtr = router(layers=self.m3_stack,
bbox=bbox,
design=self)
rtr.route(pins_to_route)
if route_option == "classic":
from openram.router import signal_escape_router as router
# mod Use for control which edge/position the pins(dout) will be placed
# 0 -> default
# 1 -> all top/bottom
# 2 -> all left/right
rtr = router(layers=self.m3_stack,
bbox=bbox,
design=self,
mod=mod)
rtr.route(self.pins_to_route)
elif route_option == "quality":
# use io_pin_placer
# put the IO pins at the edge
from openram.router.io_pin_placer import io_pin_placer as placer
pl = placer(layers=self.m3_stack,
bbox=bbox,
design=self)
for name in self.pins_to_route:
debug.warning("pins_to_route pins -> {0}".format(name))
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 """
@ -779,10 +832,10 @@ class sram_1bank(design, verilog, lef):
else:
self.num_spare_cols = 0
def place_instances(self):
def place_instances_changeable(self, position_add=0):
"""
This places the instances for a single bank SRAM with control
logic and up to 2 ports.
logic and up to 2 ports, but be able to change the io the dff position
"""
# No orientation or offset
@ -844,6 +897,9 @@ class sram_1bank(design, verilog, lef):
self.route_dffs(add_routes=False)
self.remove_layout_pins()
for port in self.all_ports:
# Add the extra position
self.data_bus_size[port] += position_add
# Re-place with the new channel size
self.place_dffs()
@ -856,7 +912,6 @@ class sram_1bank(design, verilog, lef):
x_offset = self.control_logic_insts[port].rx() - self.row_addr_dff_insts[port].width
# It is above the control logic and the predecoder array
y_offset = max(self.control_logic_insts[port].uy(), self.bank.predecoder_top)
self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port])
@ -865,7 +920,7 @@ class sram_1bank(design, verilog, lef):
# The row address bits are placed above the control logic aligned on the left.
x_offset = self.control_pos[port].x - self.control_logic_insts[port].width + self.row_addr_dff_insts[port].width
# If it can be placed above the predecoder and below the control logic, do it
y_offset = self.bank.predecoder_bottom
y_offset = min(self.control_logic_insts[port].by(), self.bank.predecoder_bottom)
self.row_addr_pos[port] = vector(x_offset, y_offset)
self.row_addr_dff_insts[port].place(self.row_addr_pos[port], mirror="XY")
@ -1051,7 +1106,7 @@ class sram_1bank(design, verilog, lef):
"spare_wen{0}[{1}]".format(port, bit),
start_layer=pin_layer)
def route_layout(self):
def route_layout(self, mod=0, route_option="classic"):
""" Route a single bank SRAM """
self.route_clk()
@ -1067,7 +1122,7 @@ class sram_1bank(design, verilog, lef):
self.add_layout_pins()
# Some technologies have an isolation
self.add_dnwell(inflate=2.5)
self.moat_pins = self.add_dnwell(inflate=2.5, route_option=route_option)
init_bbox = self.get_bbox()
# Route the supplies together and/or to the ring/stripes.
@ -1075,10 +1130,13 @@ class sram_1bank(design, verilog, lef):
if OPTS.perimeter_pins:
# We now route the escape routes far enough out so that they will
# reach past the power ring or stripes on the sides
self.route_escape_pins(init_bbox)
if OPTS.route_supplies:
self.route_supplies(init_bbox)
self.route_escape_pins(bbox=init_bbox, mod=mod, route_option=route_option)
if OPTS.route_supplies:
if route_option == "classic":
self.route_supplies(init_bbox)
elif route_option == "quality": # quality
self.route_supplies_constructive(init_bbox)
def route_dffs(self, add_routes=True):
@ -1114,11 +1172,12 @@ class sram_1bank(design, verilog, lef):
if port == 0:
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
- self.data_bus_size[port] + 2 * self.m3_pitch)
self.bank_inst.by() - self.col_addr_size * self.m3_pitch)# higher offset to avoid possible overlap with data dffs channel routing
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
parent=self,
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
# This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices.
self.add_inst(cr.name, cr)
@ -1130,7 +1189,8 @@ class sram_1bank(design, verilog, lef):
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
parent=self,
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
# This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices.
self.add_inst(cr.name, cr)
@ -1165,23 +1225,18 @@ class sram_1bank(design, verilog, lef):
route_map.extend(list(zip(bank_pins, dff_pins)))
if len(route_map) > 0:
# This layer stack must be different than the column addr dff layer stack
layer_stack = self.m3_stack
layer_stack = self.m2_stack
if port == 0:
# This is relative to the bank at 0,0 or the s_en which is routed on M3 also
if "s_en" in self.control_logic_insts[port].mod.pin_map:
y_bottom = min(0, self.control_logic_insts[port].get_pin("s_en").by())
else:
y_bottom = 0
y_offset = y_bottom - self.data_bus_size[port] + 2 * self.m3_pitch
# for port 0, the offset of first track in channel router is fixed
y_offset = self.data_dff_insts[port].uy() + 6 * self.m3_pitch
offset = vector(self.control_logic_insts[port].rx() + self.dff.width,
y_offset)
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
parent=self,
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
if add_routes:
# This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices.
@ -1189,19 +1244,84 @@ class sram_1bank(design, verilog, lef):
self.connect_inst([])
# self.add_flat_inst(cr.name, cr)
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
# return the real channel width that min. should be
# the bottom of w_en or s_en
if ("s_en" in self.control_logic_insts[port].mod.pin_map) and ("w_en" in self.control_logic_insts[port].mod.pin_map):# rw
y_bottom = min(0, self.control_logic_insts[port].get_pin("s_en").by(), self.control_logic_insts[port].get_pin("w_en").by())
elif "w_en" in self.control_logic_insts[port].mod.pin_map:# w
y_bottom = min(0, self.control_logic_insts[port].get_pin("w_en").by())
else:
y_bottom = 0
if len(self.all_ports) == 1: # only 1 port at bottom
extra_offset = 6 * self.m3_pitch + (self.bank_inst.by() - (y_bottom - self.m4_nonpref_pitch))
else: # 2 ports, row address decoder needs to be considered
# for port 0
# determine the most right dffs
if self.num_spare_cols: # if we have spare regs
dff_right_x = self.spare_wen_dff_insts[0].rx()
else: # data dffs
dff_right_x = self.data_dff_insts[0].rx()
# check if row address dffs are overlaped with dff area, bank position as reference
if self.bank_inst.rx() < (dff_right_x + 2 * self.m4_pitch):
debug.warning("m4 pitch ----> {0}".format(self.m4_pitch))
debug.warning("m3 pitch ----> {0}".format(self.m3_pitch))
debug.warning("m4_non_pref pitch ----> {0}".format(self.m4_nonpref_pitch))
debug.warning("lower row addr dff: {0}".format(self.row_addr_dff_insts[1].by()))
debug.warning("higher row addr dff: {0}".format(self.row_addr_dff_insts[1].uy()))
# check the most lower one betwenn control signal and address dff
if y_bottom < self.row_addr_dff_insts[1].by():
y_bottom_most = y_bottom
else:
y_bottom_most = self.row_addr_dff_insts[1].by()
# the upper track should below the lower one
extra_offset = 6 * self.m3_pitch + (self.bank_inst.by() - (y_bottom_most - self.m4_nonpref_pitch))
else: # do not need take care of address dff 1, since it's far away
extra_offset = 6 * self.m3_pitch + (self.bank_inst.by() - (y_bottom - self.m4_nonpref_pitch))
debug.warning("extra_offset->{0}".format(extra_offset))
debug.warning("channel_height->{0}".format(cr.height))
debug.warning("self.col_addr_bus_size->{0}".format(self.col_addr_bus_size[port]))
debug.warning("self.databusgap->{0}".format(self.data_bus_gap))
self.data_bus_size[port] = max((cr.height + extra_offset), self.col_addr_bus_size[port]) + self.data_bus_gap
else:
if "s_en" in self.control_logic_insts[port].mod.pin_map:
y_top = max(self.bank.height, self.control_logic_insts[port].get_pin("s_en").uy())
# for port1, the offset of first track in channel router needs to check first, make sure no overlap with control signal & address dff
if ("s_en" in self.control_logic_insts[port].mod.pin_map) and ("w_en" in self.control_logic_insts[port].mod.pin_map):
y_top = max(self.bank.height, self.control_logic_insts[port].get_pin("s_en").uy(), self.control_logic_insts[port].get_pin("w_en").uy())
y_offset = y_top + 6 * self.m3_pitch
elif "w_en" in self.control_logic_insts[port].mod.pin_map:
y_top = max(self.bank.height, self.control_logic_insts[port].get_pin("w_en").uy())
y_offset = y_top + self.m3_pitch# it's fine, since w port doesn't have dout signals
else:
y_top = self.bank.height
y_offset = y_top + self.m3_pitch
y_offset = y_top + 6 * self.m3_pitch
# check the offset overlap with address dff 0 or not
if self.num_spare_cols: # if we have spare regs
dff_left_x = self.spare_wen_dff_insts[1].lx()
else: # data dffs
dff_left_x = self.data_dff_insts[1].lx()
# check if row address dffs are overlaped with dff area
if self.bank_inst.lx() > (dff_left_x - 2 * self.m4_pitch):
debug.warning("m4 pitch ----> {0}".format(self.m4_pitch))
debug.warning("m3 pitch ----> {0}".format(self.m3_pitch))
debug.warning("m4_non_pref pitch ----> {0}".format(self.m4_nonpref_pitch))
# the bottom track should also above row address decoder
if y_offset > self.row_addr_dff_insts[0].uy() + self.m4_nonpref_pitch:
# do not need change since first track is high enough
extra_offset = y_offset - self.bank.height # height could be use since bank at 0,0
else: # make it higher tham row address decoder
extra_offset = self.row_addr_dff_insts[0].uy() + self.m4_nonpref_pitch - self.bank_inst.height
# update the new y_offset
y_offset = self.row_addr_dff_insts[0].uy() + self.m4_nonpref_pitch
else: # do not need to take care address dff0, since it's far away
extra_offset = y_offset - self.bank.height # height could be use since bank at 0,0
offset = vector(0,
y_offset)
cr = channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack,
parent=self)
parent=self,
dff_area=True)# this is a special value to handle dff areas, should be true when routing col_dff/dffs
if add_routes:
# This causes problem in magic since it sometimes cannot extract connectivity of instances
# with no active devices.
@ -1209,7 +1329,10 @@ class sram_1bank(design, verilog, lef):
self.connect_inst([])
# self.add_flat_inst(cr.name, cr)
else:
self.data_bus_size[port] = max(cr.height, self.col_addr_bus_size[port]) + self.data_bus_gap
# return the real channel width that min. should be
# 2 ports, row address decoder needs to be considered
# for port 1
self.data_bus_size[port] = max((cr.height + extra_offset), self.col_addr_bus_size[port]) + self.data_bus_gap
def route_clk(self):
""" Route the clock network """

View File

@ -52,6 +52,8 @@ class options(optparse.Values):
words_per_row = None
num_spare_rows = 0
num_spare_cols = 0
# Route approach
route_approach = "classic"# "classic" or "quality"
###################
# ROM configuration options

View File

@ -0,0 +1,449 @@
# 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.base.vector3d import vector3d
from openram import OPTS
from .graph import graph
from .graph_shape import graph_shape
from .router import router
import re
class io_pin_placer(router):
def __init__(self, layers, design, bbox=None):
# `router` is the base router class
router.__init__(self, layers, design, bbox)
# New pins are the side supply pins
self.new_pins = {}
# added_io_pins
self.io_pins_added_left = []
self.io_pins_added_right = []
self.io_pins_added_up = []
self.io_pins_added_down = []
# fake_pins, use for rename
self.io_pins_fake =[]
def get_closest_edge(self, point):
""" Return a point's the closest edge and the edge's axis direction. """
ll, ur = self.bbox
# 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", True
if min_diff == ll_diff_y:
return "bottom", False
if min_diff == ur_diff_x:
return "right", True
return "top", False
def initial_position(self, pins): # pins is list []
""" Set the IO pin center at the perimeter """
pattern_clk = r'^clk'
pattern_addr0 = r'^addr0'
pattern_addr1 = r'^addr1'
pattern_dout0 = r'^dout0'
pattern_dout1 = r'^dout1'
pattern_din = r'^din'
pattern_wmask = r'^wmask'
pattern_spare_wen = r'^spare_wen'
pattern_web = r'^web'
pattern_csb = r'^csb'
for pin in pins:
c = pin.center()
# Find the closest edge
edge, vertical = self.get_closest_edge(c)
if re.match(pattern_clk, pin.name):# clk, should be placed at vertical edge
if edge == "bottom" or edge == "left":#clk0
edge = "left"
elif edge == "top" or edge == "right":#clk1
edge = "right"
vertical = True
self.store_position(pin, edge, vertical)
if re.match(pattern_addr0, pin.name): # all the addr0[] should be placed at left edge
if edge == "top" or edge == "left":
edge = "left"
vertical = True
elif edge == "bottom": # but for big sram, addr0[] may have pins at bottom, which is allowed
vertical = False
self.store_position(pin, edge, vertical)
if re.match(pattern_addr1, pin.name): # all the addr1[] should be placed at right edge
if edge == "bottom" or edge == "right":
edge = "right"
vertical = True
elif edge == "top": # but for big sram, addr1[] may have pins at top, which is allowed
vertical = False
self.store_position(pin, edge, vertical)
if re.match(pattern_din, pin.name): # din
self.store_position(pin, edge, vertical)
if re.match(pattern_wmask, pin.name): # wmask
self.store_position(pin, edge, vertical)
if re.match(pattern_spare_wen, pin.name): # spare_wen
self.store_position(pin, edge, vertical)
if re.match(pattern_csb, pin.name):# csb
self.store_position(pin, edge, vertical)
if re.match(pattern_web, pin.name): # web
self.store_position(pin, edge, vertical)
# special handle the dout pins, if r/rw ports
for pin in pins:
if re.match(pattern_dout0, pin.name):
edge = "bottom"
vertical = False
self.store_dout_position(pin, edge, vertical)
if re.match(pattern_dout1, pin.name):
edge = "top"
vertical = False
self.store_dout_position(pin, edge, vertical)
def check_overlap(self, pin_position, edge):
""" Return the suggested position to aviod overlap """
ll, ur = self.bbox
c = pin_position # original source pin center
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
add_distance = 0
if edge == "bottom":
fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
via_to_close = False
# if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias
while pin_to_close or via_to_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.1
fake_center = vector(c.x + add_distance, ll.y - self.track_wire * 2 + offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_down)
via_to_close = abs(fake_center.x - c.x) < 0.6
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
via_to_close = False
# if cannot direct place below the source pin, need move towards right, and ensure the min. distance between vias
while pin_to_close or via_to_close:
debug.warning("overlap, changing position")
add_distance = add_distance + 0.1
fake_center = vector(c.x + add_distance, ur.y + self.track_wire * 2 - offset)
pin_to_close = any(abs(pin_added.center().x - fake_center.x) < (0.4 + self.half_wire * 4)for pin_added in self.io_pins_added_up)
via_to_close = abs(fake_center.x - c.x) < 0.6
return fake_center
def store_dout_position(self, pin, edge, vertical):
pin_position = pin.center()
pin_position = self.check_overlap(pin_position, edge)
# store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough
layer = self.get_layer(int(not vertical))
half_wire_vector = vector([self.half_wire] * 2)
nll = pin_position - half_wire_vector - half_wire_vector
nur = pin_position + half_wire_vector + half_wire_vector
rect = [nll, nur]
#fake_pin = [pin.name() + "_" + "fake", pin_position, rect, layer]
fake_pin = graph_shape(name=pin.name + "_" + "fake",
rect=rect,
layer_name_pp=layer)
if edge == "left":
self.io_pins_added_left.append(fake_pin)
elif edge == "bottom":
self.io_pins_added_down.append(fake_pin)
elif edge == "right":
self.io_pins_added_right.append(fake_pin)
elif edge == "top":
self.io_pins_added_up.append(fake_pin)
self.io_pins_fake.append(fake_pin)
debug.warning("pin added: {0}".format(fake_pin))
def store_position(self, pin, edge, vertical): # also need to store the source pin
ll, ur = self.bbox
c = pin.center()
pattern_clk = r'^clk'
pattern_csb = r'^csb'
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
if edge == "left":
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y)
if re.match(pattern_clk, pin.name): # clk0 need to be higher at left edge, 0.32 is magic number
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y + 0.32)
if re.match(pattern_csb, pin.name): # csb0 need to be lower at left edge, 0.32 is magic number
fake_center = vector(ll.x - self.track_wire * 2 + offset, c.y - 0.32)
if edge == "bottom":
fake_center = vector(c.x, ll.y - self.track_wire * 2 + offset)
if edge == "right":
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y)
if re.match(pattern_clk, pin.name): # clk1 need to be lower at right edge, 0.32 is magic number
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y - 0.32)
if re.match(pattern_csb, pin.name): # csb0 need to be higher at right edge, 0.32 is magic number
fake_center = vector(ur.x + self.track_wire * 2 - offset, c.y + 0.32)
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2 - offset)
# store the center position, rect, layer of fake pin, here make sure the pin in the gds will be big enough
layer = self.get_layer(int(not vertical))
half_wire_vector = vector([self.half_wire] * 2)
nll = fake_center - half_wire_vector - half_wire_vector
nur = fake_center + half_wire_vector + half_wire_vector
rect = [nll, nur]
#fake_pin = [pin.name() + "_" + "fake", fake_center, rect, layer]
fake_pin = graph_shape(name=pin.name + "_" + "fake",
rect=rect,
layer_name_pp=layer)
if edge == "left":
self.io_pins_added_left.append(fake_pin)
elif edge == "bottom":
self.io_pins_added_down.append(fake_pin)
elif edge == "right":
self.io_pins_added_right.append(fake_pin)
elif edge == "top":
self.io_pins_added_up.append(fake_pin)
self.io_pins_fake.append(fake_pin)
debug.warning("pin added: {0}".format(fake_pin))
def add_io_pins(self, pin_names):
""" Add IO pins on the edges WITHOUT routing them. """
debug.info(1, "Adding IO pins on the perimeter...")
# Prepare GDS reader (if necessary for pin/blockage identification)
self.prepare_gds_reader()
# Find pins to be added (without routing)
for name in pin_names:
self.find_pins(name)# this will add the pins to the self.pins
# inital position
pin_list = []
for name in self.pins:
pin = next(iter(self.pins[name]))
pin_list.append(pin)
self.initial_position(pin_list)
# Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]"
self.replace_layout_pins(pin_names)
def add_io_pins_connected(self, pin_names):
""" Add IO pins on the edges WITH routing them. """
debug.info(1, "Adding IO pins on the perimeter...")
# Prepare GDS reader (if necessary for pin/blockage identification)
self.prepare_gds_reader()
# Find pins to be added (without routing)
for name in pin_names:
self.find_pins(name)# this will add the pins to the self.pins
debug.warning("the pins in pin_name -> {0}".format(name))
# inital position
pin_list = []
for name in self.pins:
pin = next(iter(self.pins[name]))
pin_list.append(pin)
debug.warning("the pins in self.pins -> {0}".format(name))
self.initial_position(pin_list)
# add fake io pins at the perimeter, which will be used for routing
for fake_pin in self.io_pins_fake:
self.design.add_layout_pin(text=fake_pin.name,
layer=fake_pin.layer,
offset=fake_pin.ll(),
width=fake_pin.width(),
height=fake_pin.height())
# connect the source_pin and io_pin(target)
self.connect_pins(pin_names)
# remove the fake pin before change the name, in order to avoid possible problem
for fake_pin in self.io_pins_fake:
self.remove_io_pins(fake_pin.name)
# Change IO pin names, which means "internal name" will be used, and internal io pins will not have label such as "dout0[0]"
self.replace_layout_pins(pin_names)
def connect_pins(self, pin_names): # pin_names should be a list
""" Add IO pins on the edges, and connect them to the internal one, not-graph like process """
debug.info(1, "connecting to io pins...")
pattern_dout = r'^dout'
for pin_name in pin_names:
# get pin pairs ready
source_pin = next(iter(self.pins[pin_name]))
for fake_pin in self.io_pins_fake:
if pin_name + "_" + "fake" == fake_pin.name:
target_pin = fake_pin
break
# special hanlde dout pins
if re.match(pattern_dout, pin_name):
number_str = re.findall(r'\[(\d+)\]', pin_name)
if number_str:
number = int(number_str[0])
if number % 2 == 0:
is_up = True
else:
is_up = False
point_list = self.decide_point(source_pin, target_pin, is_up)
self.add_wire(point_list)
# other pins
else:
debug.warning("source{0}".format(source_pin.center()))
debug.warning("target{0}".format(target_pin.center()))
point_list = self.decide_point(source_pin, target_pin)
debug.warning("point_list->{0}".format(point_list))
self.add_wire(point_list)
def add_wire(self, point_list):
if len(point_list) == 2:
# direct connect
self.add_line(point_list[0], point_list[1])
elif len(point_list) == 4:
# intermediate points
self.add_line(point_list[0], point_list[1])
self.add_via(point_list[1])
self.add_line(point_list[1], point_list[2])
self.add_via(point_list[2])
self.add_line(point_list[2], point_list[3])
def add_line(self, point_1, point_2):
if round(point_1.y, 3) == round(point_2.y, 3):
# horizontal m3
self.design.add_path(self.get_layer(False), [point_1, point_2])
else:
# vertical m4
self.design.add_path(self.get_layer(True), [point_1, point_2])
def add_via(self, point):
# currently only m3-m4 vias are supported in this method
# usd in order to make "z" shape routing only
self.design.add_via_stack_center(from_layer=self.get_layer(False),
to_layer=self.get_layer(True),
offset=point)
def add_start_via(self, point):
# currently only m3-m4 vias are supported in this method
# used in source_pin only
self.design.add_via_stack_center(from_layer=self.get_layer(False),
to_layer=self.get_layer(True),
offset=point)
def add_big_plate(self, layer, offset, width, height):
# add rectagle at internal source pin, which avoid jog/non-preferred routing, but could implement shift-routing
# used in source_pin only
# offset->vertor(...), it is bottom-left position
self.design.add_rect(layer=layer,
offset=offset,
width=width,
height=height)
def decide_point(self, source_pin, target_pin, is_up=False):
ll, ur = self.bbox
offset = 0.95 + 0.19 # FIX: this is the magic number to overcome the ovetflow problem at the boundary, may need a method
pattern_clk = r'^clk'
pattern_csb = r'^csb'
# internal -> left
if round(target_pin.center().x, 3) == round(ll.x - self.track_wire * 2 + offset, 3):
# special handle clk0
if re.match(pattern_clk, source_pin.name):
return [vector(source_pin.rc().x, source_pin.rc().y + 0.32), target_pin.lc()]# 0.32 should be same in initial_position
# special handel csb0
elif re.match(pattern_csb, source_pin.name):
return [vector(source_pin.rc().x, source_pin.rc().y - 0.32), target_pin.lc()]# 0.32 should be same in initial_position
else:
# direct connect possible
return [source_pin.rc(), target_pin.lc()] # FIX: not sure if shape overlap in met3 allowed, but seems OK
# internal -> right
if round(target_pin.center().x, 3) == round(ur.x + self.track_wire * 2 - offset, 3):
# special handel clk1
if re.match(pattern_clk, source_pin.name):
return [vector(source_pin.lc().x, source_pin.lc().y - 0.32), target_pin.rc()]# 0.32 should be same in initial_position
# special handle csb0
elif re.match(pattern_csb, source_pin.name):
return [vector(source_pin.lc().x, source_pin.lc().y + 0.32), target_pin.rc()]# 0.32 should be same in initial_position
else:
# direct connect possible
return [source_pin.lc(), target_pin.rc()]
# internal -> top, need to add start_via m3->m4
if round(target_pin.center().y, 3) == round(ur.y + self.track_wire * 2 - offset, 3):
self.add_start_via(source_pin.center())
if round(target_pin.center().x, 3) == round(source_pin.center().x, 3):
# direct connect possible
return [source_pin.bc(), target_pin.uc()]
else:
# need intermediate point
#via_basic_y = self.design.bank.height + 3 # 3 is magic number, make sure out of bank area
via_basic_y = self.design.bank_inst.uy() + 3 * self.design.m3_pitch
is_up = not is_up# Be attention, for channel at the top, the is_up should be inverted! Otherwise will cause overlap!
if is_up:
#via_basic_y = via_basic_y + 0.5
via_basic_y = via_basic_y + self.design.m3_pitch
else:
#via_basic_y = via_basic_y - 0.5
via_basic_y = via_basic_y - self.design.m3_pitch
point_1 = vector(source_pin.center().x, via_basic_y)
point_2 = vector(target_pin.center().x, via_basic_y)
return [source_pin.bc(), point_1, point_2, target_pin.uc()]
# internal -> bottom, need to add start_via m3->m4
if round(target_pin.center().y, 3) == round(ll.y - self.track_wire * 2 + offset, 3):
self.add_start_via(source_pin.center())
if round(target_pin.center().x, 3) == round(source_pin.center().x, 3):
# direct connect possible
return [source_pin.uc(), target_pin.bc()]
else:
# need intermediate point
#via_basic_y = ll.y + 22 # 22 is magic number, make sure out of dff area
via_basic_y = self.design.data_dff_insts[0].uy() + 3 * self.design.m3_pitch
if is_up:
#via_basic_y = via_basic_y + 0.5
via_basic_y = via_basic_y + self.design.m3_pitch
else:
#via_basic_y = via_basic_y - 0.5
via_basic_y = via_basic_y - self.design.m3_pitch
point_1 = vector(source_pin.center().x, via_basic_y)
point_2 = vector(target_pin.center().x, via_basic_y)
return [source_pin.uc(), point_1, point_2, target_pin.bc()]
def remove_io_pins(self, pin_name):
# remove io pin in gds, so we could reroute
self.design.remove_layout_pin(pin_name)
def replace_layout_pins(self, pin_names):
""" Change the IO pin names with new ones around the perimeter. """
for pin_name in pin_names:
perimeter_pin_name = pin_name + "_" + "fake"
for pin in self.io_pins_fake:
if pin.name == perimeter_pin_name:
perimeter_pin = pin
self.design.replace_layout_pin(pin_name, perimeter_pin)
break

View File

@ -111,6 +111,52 @@ 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 set and return
""" Find the pins with the given name. """
debug.info(4, "Finding all pins for {}".format(pin_name))
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):
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)
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

@ -89,6 +89,8 @@ class router_tech:
# When we actually create the routes, make them the width of the track (minus 1/2 spacing on each side)
self.layer_widths = [self.track_wire, 1, self.track_wire]
# via2 to via3 distance requirements
self.via2_via3_pitch = 0.5 * drc("minwidth_{}".format("via2")) + 0.5 * drc("minwidth_{}".format("via3")) + drc["via3_to_via2"]
def same_lpp(self, lpp1, lpp2):
"""

View File

@ -10,14 +10,14 @@ from openram import OPTS
from .graph import graph
from .graph_shape import graph_shape
from .router import router
import re
class signal_escape_router(router):
"""
This is the signal escape router that uses the Hanan grid graph method.
"""
def __init__(self, layers, design, bbox=None):
def __init__(self, layers, design, bbox=None, mod=0):
# `router` is the base router class
router.__init__(self, layers, design, bbox)
@ -25,6 +25,17 @@ class signal_escape_router(router):
# New pins are the side supply pins
self.new_pins = {}
# Use for add distance of dout pins at the perimeter
self.distance_right = 0
self.distance_left = 0
# Use for control which edge/position the pins(dout) will be placed
# 0 -> default
# 1 -> all top/bottom
# 2 -> all left/right
self.state_mod = mod
def route(self, pin_names):
""" Route the given pins to the perimeter. """
@ -176,7 +187,6 @@ class signal_escape_router(router):
layer_name_pp=layer)
self.fake_pins.append(pin)
def create_fake_pin(self, pin):
""" Create a fake pin on the perimeter orthogonal to the given pin. """
@ -197,15 +207,48 @@ class signal_escape_router(router):
if edge == "top":
fake_center = vector(c.x, ur.y + self.track_wire * 2)
# relocate the pin position
pattern = r'^dout'
if re.match(pattern, pin.name):
if self.state_mod == 0:
pass# do not change, default
elif self.state_mod == 1: # all top/bottom
if edge == "right":
vertical = False
fake_center = vector(c.x, ll.y - self.track_wire * 2)
self.distance_right += 1
else:
if edge == "left":
vertical = False
fake_center = vector(c.x, ll.y + self.track_wire * 2)
self.distance_left += 1
elif self.state_mod == 2: # all left/right
if (edge == "bottom") or (edge == "right"):# change to the east
vertical = True
fake_center = vector(ur.x + self.track_wire * 2, ll.y + 30 + self.distance_right)
self.distance_right += 1
else:
if (edge == "top") or (edge == "left"):# change to the west
vertical = True
fake_center = vector(ll.x - self.track_wire * 2, ur.y - 30 - self.distance_left)
self.distance_left += 1
else:
debug.error("wrong state mod!", -1)
# Create the fake pin shape
layer = self.get_layer(int(not vertical))
half_wire_vector = vector([self.half_wire] * 2)
nll = fake_center - half_wire_vector
nur = fake_center + half_wire_vector
rect = [nll, nur]
pin = graph_shape(name="fake",
rect=rect,
layer_name_pp=layer)
return pin

View File

@ -0,0 +1,805 @@
# 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", moat_pins=None):
# `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 = moat_pins
# store a graphshape of intermediate points(if shift)/source points(if no shift) when connecting moat_pins to outside
# trick: since in the creation of dnwell, these pins are "ordered" added, so they'are also ordered here
# order inside list: left -> right or bottom -> up
self.moat_pins_left = []
self.moat_pins_right = []
self.moat_pins_top = []
self.moat_pins_bottom = []
# io pins
self.io_pins_left = []
self.io_pins_right = []
self.io_pins_top = []
self.io_pins_bottom = []
def route_outside(self, vdd_name="vdd", gnd_name="gnd", io_pin_names=None):
# only connect supply with inside submodules, not connecting to the power ring
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.find_pins_inside(vdd_name)
self.find_pins_inside(gnd_name)
self.route_moat(io_pin_names)
# 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))
# Prepare the selected moat pins
selected_moat_pins = self.prepare_selected_moat_pins()
# Route vdd and gnd
routed_count = 0
routed_max = len(self.pins[vdd_name]) + len(self.pins[gnd_name]) + len(self.moat_pins) + len(self.new_pins["gnd"])
for pin_name in [vdd_name, gnd_name]:
if pin_name == gnd_name: # otherwise will not recognaize the moat blocakge
self.prepare_gds_reader()
# 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()
pins = self.pins[pin_name]
# Route closest pins according to the minimum spanning tree
for source, target in self.get_mst_with_ring(list(pins), selected_moat_pins, pin_name):
# 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))
# finsih
self.replace_layout_pins()
def route_inside(self, vdd_name="vdd", gnd_name="gnd"):
# only connect supply with inside submodules, not connecting to the power ring
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.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))
# finsih
self.replace_layout_pins()
def route_moat(self, io_pin_names):
# route the vdd pins at the moat
# io_pin_names is a list
# the moat vdd shape will also be created and stored in the list
for moat_pin in self.moat_pins:
self.check_overlap(moat_pin, io_pin_names)
def replace_layout_pins(self):
# clear all the inside "vdd " "gnd" at sram module level
# Copy the pin shape(s) to rectangles
for pin_name in ["vdd", "gnd"]:
# Copy the pin shape(s) to rectangles
for pin in self.design.get_pins(pin_name):
self.design.add_rect(pin.layer,
pin.ll(),
pin.width(),
pin.height())
# Remove the pin shape(s)
self.design.remove_layout_pin(pin_name)
# Get new pins, change the name of ring to extern supply name
# vccd1 ring
pins = self.get_new_pins("vdd")
for pin in pins:
self.design.add_layout_pin(self.ext_vdd_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
# vssd1 ring
pins = self.get_new_pins("gnd")
for pin in pins:
self.design.add_layout_pin(self.ext_gnd_name,
pin.layer,
pin.ll(),
pin.width(),
pin.height())
def prepare_selected_moat_pins(self):
""" Selcet the possibe moat pins, feed into the MST, where will decide which of these pin should be connected to which pin """
if len(self.design.all_ports) > 1:
# in order to save runtime
# top -> moat pins all
# bottom -> moat pins all
# left -> moat pins near control logic
# right -> moat pins near control logic
# expected connection for control logic
# for port 0 -> left
start_y = self.design.control_logic_insts[0].by()# bottom edge y value
end_y = self.design.control_logic_insts[0].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y]
# for port 1 -> right
start_y = self.design.control_logic_insts[1].by()# bottom edge y value
end_y = self.design.control_logic_insts[1].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_right = [pin for pin in self.moat_pins_right if start_y <= pin.center().y <= end_y]
# return the selected moat pins
selected_moat_pins = []
selected_moat_pins.extend(filtered_moat_pins_left)
selected_moat_pins.extend(self.moat_pins_bottom)
selected_moat_pins.extend(filtered_moat_pins_right)
selected_moat_pins.extend(self.moat_pins_top)
return selected_moat_pins
else: # only 1 port
# in order to save runtime
# top -> moat pins all
# bottom -> moat pins all
# left -> moat pins near control logic
# right -> moat pins all
start_y = self.design.control_logic_insts[0].by()# bottom edge y value
end_y = self.design.control_logic_insts[0].uy()# up edge y value
# filter the pin in the range
filtered_moat_pins_left = [pin for pin in self.moat_pins_left if start_y <= pin.center().y <= end_y]
# return the selected moat pins
selected_moat_pins = []
selected_moat_pins.extend(filtered_moat_pins_left)
selected_moat_pins.extend(self.moat_pins_bottom)
selected_moat_pins.extend(self.moat_pins_right)
selected_moat_pins.extend(self.moat_pins_top)
return selected_moat_pins
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 connect them
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":
add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only
pin_too_close = any(abs(io_pin.center().x - source_center.x) < (self.track_width + 0.1) for io_pin in self.io_pins_bottom)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # right shift
tmp_center = vector((tmp_center.x + add_distance), tmp_center.y)
else: # left shift
tmp_center = vector((tmp_center.x - add_distance), tmp_center.y)
pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < (self.track_width + 0.1) for io_pin in self.io_pins_bottom)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][1] # order in list -> "top", "bottom", "right", "left"]
# bottom ring's y position at it's top
target_egde_y = vdd_ring.center().y + 0.5 * vdd_ring.height()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire))
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_bottom.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector(tmp_center.x, (target_egde_y - 0.5 * self.track_wire))
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_bottom.append(moat_pin_route)
elif edge == "top":
add_distance = self.via2_via3_pitch # if shift, need to fulfill via2-via3 spacing, top/bottom only
pin_too_close = any(abs(io_pin.center().x - source_center.x) < (self.track_width + 0.1) for io_pin in self.io_pins_top)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # right shift
tmp_center = vector((tmp_center.x + add_distance), tmp_center.y)
else: # left shift
tmp_center = vector((tmp_center.x - add_distance), tmp_center.y)
pin_too_close = any(abs(io_pin.center().x - tmp_center.x) < (self.track_width + 0.1) for io_pin in self.io_pins_top)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][0] # order in list -> "top", "bottom", "right", "left"]
# top ring's y position at it's bottom
target_egde_y = vdd_ring.center().y - 0.5 * vdd_ring.height()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire))
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_top.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector(tmp_center.x, (target_egde_y + 0.5 * self.track_wire))
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=True)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m4")
self.moat_pins_top.append(moat_pin_route)
elif edge == "left":
pin_too_close = any(abs(io_pin.center().y - source_center.y) < (self.track_width + 0.1) for io_pin in self.io_pins_left)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # up shift
tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance))
else: # down shift
tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance))
pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < (self.track_width + 0.1) for io_pin in self.io_pins_left)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][3] # order in list -> "top", "bottom", "right", "left"]
# left ring's x position at it's right
target_egde_x = vdd_ring.center().x + 0.5 * vdd_ring.width()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y)
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_left.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector((target_egde_x - 0.5 * self.track_wire), tmp_center.y)
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_left.append(moat_pin_route)
else: #right
pin_too_close = any(abs(io_pin.center().y - source_center.y) < (self.track_width + 0.1) for io_pin in self.io_pins_right)
tmp_center = vector(source_center.x, source_center.y)
while pin_too_close:
tmp_center = vector(source_center.x, source_center.y)
add_distance = add_distance + 0.1
if direction == 1: # up shift
tmp_center = vector(tmp_center.x, (tmp_center.y + add_distance))
else: # down shift
tmp_center = vector(tmp_center.x, (tmp_center.y - add_distance))
pin_too_close = any(abs(io_pin.center().y - tmp_center.y) < (self.track_width + 0.1) for io_pin in self.io_pins_right)
direction = - direction
# the nearst vdd ring
vdd_ring = self.new_pins["vdd"][2] # order in list -> "top", "bottom", "right", "left"]
# right ring's y position at it's left
target_egde_x = vdd_ring.center().x - 0.5 * vdd_ring.width()
if tmp_center == source_center: # no overlap
# no jog, direct return the source/target center position
# the target center position, should consider enought space for via
target_point = vector((target_egde_x + 0.5 * self.track_wire), tmp_center.y)
source_point = vector(tmp_center.x, tmp_center.y)
point_list = [source_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(source_point.x - 0.5 * self.track_wire, source_point.y - 0.5 * self.track_wire)
ur = vector(source_point.x + 0.5 * self.track_wire, source_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_right.append(moat_pin_route)
else: # need jog
# shift the center
# add rectangle at same layer (original)
intermediate_point = vector(tmp_center.x, tmp_center.y)
source_point = vector(source_center.x, source_center.y)
target_point = vector((target_egde_x + 0.5 * self.track_wire) ,tmp_center.y)
point_list = [source_point, intermediate_point, target_point]
self.add_wire(point_list, vertical=False)
# store the shape of moat pins, need for route later
ll = vector(intermediate_point.x - 0.5 * self.track_wire, intermediate_point.y - 0.5 * self.track_wire)
ur = vector(intermediate_point.x + 0.5 * self.track_wire, intermediate_point.y + 0.5 * self.track_wire)
rect = [ll, ur]
moat_pin_route = graph_shape("vdd", rect, "m3")
self.moat_pins_right.append(moat_pin_route)
def add_wire(self, point_list, vertical=False):
if vertical == True: # m4 line, need start via3(m3 -> m4), end via3(m3 -> m4)
if len(point_list) == 2: # direct connect
# start via
self.add_via(point=point_list[0],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[0],
from_layer="m4",
to_layer="m4") # shape
# connection
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m4")
# end via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m4",
to_layer="m4") # shape
elif len(point_list) == 3: # need intermediate point
# jog
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m3")
# start_via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[1],
point_2=point_list[2],
layer="m4")
# end via
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[2],
from_layer="m4",
to_layer="m4") # shape
else: # m3 line, need start via2(m2 -> m3), end via3(m3 -> m4)
if len(point_list) == 2: # direct connect
# start via
self.add_via(point=point_list[0],
from_layer="m2",
to_layer="m3")
self.add_via(point=point_list[0],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m3")
# end via
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
elif len(point_list) == 3: # need intermediate point
# jog
self.add_line(point_1=point_list[0],
point_2=point_list[1],
layer="m2")
# start_via
self.add_via(point=point_list[1],
from_layer="m2",
to_layer="m3")
self.add_via(point=point_list[1],
from_layer="m3",
to_layer="m3") # shape
# connection
self.add_line(point_1=point_list[1],
point_2=point_list[2],
layer="m3")
# end via
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m4")
self.add_via(point=point_list[2],
from_layer="m3",
to_layer="m3") # shape
def add_line(self, point_1, point_2, layer="m3"): # "m2", "m3", "m4"
self.design.add_path(layer, [point_1, point_2], self.track_wire)
def add_via(self, point, from_layer="m3", to_layer="m4"):
# via could be via2(m2 -> m3), via3(m3 -> m4)
# or a shape at same layer
if from_layer == to_layer:
self.design.add_rect_center(layer=from_layer,
offset=point,
width=self.track_wire,
height=self.track_wire)
else:
self.design.add_via_stack_center(from_layer=from_layer,
to_layer=to_layer,
offset=point)
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 = next(iter(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()
debug.warning("moat pin center -> {0}".format(point))
# 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
#debug.warning("pin -> {0}".format(pin))
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 == "vdd"
# 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)
return pin
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 = 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)
# 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_mst_with_ring(self, pins, ring_pins, pin_name="vdd"):
"""
Extend the MST logic to connect internal pins to the nearest external ring pins.
"""
# Prepare the pins that are allowed to connect to the moat pins.
# Specical handle gnd ring
candidate_pins = []
max_distance = 20#13
if pin_name == "gnd":
ring_pins = []
ring_pins = self.new_pins[pin_name]
for pin in pins:
# Special handle vdd/gnd in 1port sram(1rw), only at bank top-right is allowed
if len(self.design.all_ports) == 1:
# check if pin is inside bank area
if (self.design.bank_inst.lx() < pin.center().x < self.design.bank_inst.rx()) and (self.design.bank_inst.by() < pin.center().y < self.design.bank_inst.uy()):
# add pin at top-right as candidate, not care the distance
if (pin.center().x > self.design.bank_inst.rx() - 14) and (pin.center().y > self.design.bank_inst.uy() - 14):
candidate_pins.append(pin)
continue
else:# pin in the other area of bank, do not care
continue
# 2 port situation, or the other pins outer bank in 1port situation
for ring_pin in ring_pins:
dist = pin.distance(ring_pin)
if max_distance is None or dist <= max_distance:
candidate_pins.append(pin)
break
# Compute the MST for internal pins
mst_pairs = self.get_mst_pairs(pins)
# Connect each internal pin to the nearest external ring pin
used_ring_pins = set()
internal_to_ring_pairs = []
for pin in candidate_pins:
min_distance = float("inf")
nearest_ring_pin = None
for ring_pin in ring_pins:
if pin_name == "vdd" and ring_pin in used_ring_pins:
continue
dist = pin.distance(ring_pin)
if dist < min_distance:
min_distance = dist
nearest_ring_pin = ring_pin
# Add the connection to the nearest ring pin
if nearest_ring_pin:
internal_to_ring_pairs.append((pin, nearest_ring_pin))
# Mark the ring pin as used if the pin is VDD
if pin_name == "vdd":
used_ring_pins.add(nearest_ring_pin)
# Combine internal MST pairs and external connections
full_connections = mst_pairs + internal_to_ring_pairs
return full_connections
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

@ -51,11 +51,83 @@ class sram():
from openram.modules.sram_1bank import sram_1bank as sram
num_ports = OPTS.num_rw_ports + OPTS.num_r_ports + OPTS.num_w_ports
self.s = sram(name, sram_config)
self.s.create_netlist()# not placed & routed jet
self.s.create_netlist()
if not OPTS.netlist_only:
self.s.create_layout()
# choose the routung method, maze router or constructive
if OPTS.route_approach == "classic":
cur_state = "IDLE"
if not OPTS.netlist_only:
i = 0
while i < (OPTS.word_size + 100):
debug.warning("current state: state = {0}".format(cur_state))
if cur_state == "IDLE":# default, fisrt try
try:
self.s.create_layout(position_add=i)
except AssertionError as e:
cur_state = "ALL_TOP_BOTTOM"
del self.s
self.s = sram(name, sram_config)
self.s.create_netlist()
if num_ports > 1:
i = 16
else:
i = 0
continue
cur_state = "FINISH"
break
elif cur_state == "ALL_TOP_BOTTOM":
try:
self.s.create_layout(position_add=i, mod=1)
except AssertionError as e:
cur_state = "ALL_LEFT_RIGHT"
i = OPTS.word_size + 24
del self.s
self.s = sram(name, sram_config)
self.s.create_netlist()
continue
cur_state = "FINISH"
break
elif cur_state == "ALL_LEFT_RIGHT":
try:
self.s.create_layout(position_add=i, mod=2)
except AssertionError as e:
cur_state = "ALL_LEFT_RIGHT"
i = i + 1
if i == (99 + OPTS.word_size):# failed in rounting
debug.error("Failed in rounting", -1)
break
del self.s
self.s = sram(name, sram_config)
self.s.create_netlist()
continue
cur_state = "FINISH"
break
else:
cur_state = "FINISH"
break
elif OPTS.route_approach == "quality":
if not OPTS.netlist_only:
i = 0
while i < 10:
debug.warning("current i: i = {0}".format(i))
try:
self.s.create_layout(position_add=i, route_option="quality")
except AssertionError as e:
i = i + 1
if i == 9: #failed in routing
debug.error("Failed in routing", -1)
break
del self.s
self.s = sram(name, sram_config)
self.s.create_netlist()
continue
break
if not OPTS.is_unit_test:
print_time("SRAM creation", datetime.datetime.now(), start_time)
@ -110,7 +182,7 @@ class sram():
spname = OPTS.output_path + self.s.name + ".sp"
debug.print_raw("SP: Writing to {0}".format(spname))
self.sp_write(spname)
''' #comment the following state when generating big sram, and then disable drc/lvs, because maigc_ext stuck
# Save a functional simulation file with default period
functional(self.s,
spname,
@ -132,7 +204,7 @@ class sram():
d.targ_write_ports = [self.s.write_ports[0]]
d.write_delay_stimulus()
print_time("DELAY", datetime.datetime.now(), start_time)
''' #comment the above when generating big sram, and then disable drc/lvs, bevause magic_ext stuck
# Save trimmed spice file
temp_trim_sp = "{0}trimmed.sp".format(OPTS.output_path)
self.sp_write(temp_trim_sp, lvs=False, trim=True)

View File

@ -10,8 +10,10 @@ nominal_corner_only = True
#local_array_size = 16
route_supplies = "ring"
#supply_pin_type = "top"
#route_supplies = "left"
check_lvsdrc = True
#route_supplies = False
check_lvsdrc = False
uniquify = True
#perimeter_pins = False
#netlist_only = True

View File

@ -71,7 +71,6 @@ 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()
# Output the files for the resulting SRAM
s.save()

View File

@ -502,7 +502,8 @@ drc["pwell_to_nwell"] = 0
drc.add_layer("nwell",
width=0.840,
spacing=1.270)
# nwell.6 Minimum enclosure of nwell hole by deep nwell outside UHVI
drc["minclosure_nwell_by_dnwell"] = 1.030
# poly.1a Minimum width of poly
# poly.2 Minimum spacing of poly AND active
drc.add_layer("poly",
@ -662,7 +663,8 @@ drc.add_enclosure("m3",
drc.add_layer("via3",
width=0.200,
spacing=0.200)
# via3.12 Minimum spacing of via3 to via2 (cu)
drc["via3_to_via2"] = 0.180
# m4.1 Minimum width of metal4
# m4.2 Minimum spacing of metal4
# m4.7 Minimum area of metal4