mirror of https://github.com/VLSIDA/OpenRAM.git
commit
2ff7833e3b
|
|
@ -0,0 +1,20 @@
|
|||
default:
|
||||
image: ubuntu:latest
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y git wget make gcc curl
|
||||
- git checkout ci_test
|
||||
- git pull origin ci_test
|
||||
- chmod +x ./install_conda.sh
|
||||
- ./install_conda.sh
|
||||
- echo "export OPENRAM_HOME="/builds/asic_non_nda/openramenhanced/compiler"" >> ~/.bashrc
|
||||
- echo "export OPENRAM_TECH="/builds/asic_non_nda/openramenhance/technology"" >> ~/.bashrc
|
||||
- echo "export PYTHONPATH=$OPENRAM_HOME" >> ~/.bashrc
|
||||
- source ~/.bashrc
|
||||
- source miniconda/bin/activate
|
||||
- make sky130-pdk
|
||||
- make sky130-install
|
||||
script:
|
||||
- pwd
|
||||
- cd ./macros
|
||||
- make sky130_sram_1rw_tiny
|
||||
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
|
|
@ -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 """
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -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...")
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -748,6 +750,7 @@ spice["bitcell_leakage"] = 1 # Leakage power of a single bitcell in nW
|
|||
spice["inv_leakage"] = 1 # Leakage power of inverter in nW
|
||||
spice["nand2_leakage"] = 1 # Leakage power of 2-input nand in nW
|
||||
spice["nand3_leakage"] = 1 # Leakage power of 3-input nand in nW
|
||||
spice["nand4_leakage"] = 1 # Leakage power of 4-input nand in nW
|
||||
spice["nor2_leakage"] = 1 # Leakage power of 2-input nor in nW
|
||||
spice["dff_leakage"] = 1 # Leakage power of flop in nW
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue