Changes to simplify metal preferred directions and pitches.

Changes to allow decoder height to be a 2x multiple of bitcell height.
Split of control logic tests.
Fixed track spacing in SRAM and channel router
PEP8 cleanup.
This commit is contained in:
mrg 2020-05-07 12:35:21 -07:00
parent dd73afc983
commit b7c66d7e07
42 changed files with 953 additions and 953 deletions

View File

@ -47,8 +47,16 @@ class contact(hierarchy_design.hierarchy_design):
self.layer_stack = layer_stack
self.dimensions = dimensions
if directions:
# Non-preferred directions
if directions == "nonpref":
first_dir = "H" if self.get_preferred_direction(layer_stack[0])=="V" else "V"
second_dir = "H" if self.get_preferred_direction(layer_stack[2])=="V" else "V"
self.directions = (first_dir, second_dir)
# User directions
elif directions:
self.directions = directions
# Preferred directions
else:
self.directions = (tech.preferred_directions[layer_stack[0]],
tech.preferred_directions[layer_stack[2]])

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
from hierarchy_design import hierarchy_design
from utils import round_to_grid
import contact
from globals import OPTS
import re
@ -31,48 +32,98 @@ class design(hierarchy_design):
in many places in the compiler.
"""
from tech import layer_indices
import tech
for key in dir(tech):
# Single layer width rules
match = re.match(r".*_stack$", key)
if match:
for layer in layer_indices:
key = "{}_stack".format(layer)
# Set the stack as a local helper
try:
layer_stack = getattr(tech, key)
# Set the stack as a local helper
setattr(self, key, layer_stack)
except AttributeError:
pass
# Add the pitch
setattr(self,
"{}_pitch".format(layer_stack[0]),
self.compute_pitch(layer_stack))
# Skip computing the pitch for active
if layer == "active":
continue
# Add the pitch
setattr(self,
"{}_pitch".format(layer),
self.compute_pitch(layer, True))
# Add the non-preferrd pitch (which has vias in the "wrong" way)
setattr(self,
"{}_nonpref_pitch".format(layer),
self.compute_pitch(layer, False))
if False:
print("m1_pitch", self.m1_pitch)
print("m2_pitch", self.m2_pitch)
print("m3_pitch", self.m3_pitch)
from tech import preferred_directions
print(preferred_directions)
from tech import layer, layer_indices
for name in layer_indices:
if name == "active":
continue
try:
print("{0} width {1} space {2}".format(name,
getattr(self, "{}_width".format(name)),
getattr(self, "{}_space".format(name))))
print("pitch {0} nonpref {1}".format(getattr(self, "{}_pitch".format(name)),
getattr(self, "{}_nonpref_pitch".format(name))))
except AttributeError:
pass
import sys
sys.exit(1)
def compute_pitch(self, layer_stack):
def compute_pitch(self, layer, preferred=True):
"""
This is contact direction independent pitch,
i.e. we take the maximum contact dimension
This is the preferred direction pitch
i.e. we take the minimum or maximum contact dimension
"""
# Find the layer stacks this is used in
from tech import layer_stacks
pitches = []
for stack in layer_stacks:
# Compute the pitch with both vias above and below (if they exist)
if stack[0] == layer:
pitches.append(self.compute_layer_pitch(stack, preferred))
if stack[2] == layer:
pitches.append(self.compute_layer_pitch(stack[::-1], True))
return max(pitches)
def compute_layer_pitch(self, layer_stack, preferred):
(layer1, via, layer2) = layer_stack
try:
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
contact1 = getattr(contact, layer1 + "_via")
except AttributeError:
contact1 = getattr(contact, layer2 + "_via")
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
if preferred:
if self.get_preferred_direction(layer1) == "V":
contact_width = contact1.first_layer_width
else:
contact_width = contact1.first_layer_height
else:
contact1 = getattr(contact, layer1 + "_via")
max_contact = max(contact1.width, contact1.height)
layer1_space = getattr(self, layer1 + "_space")
layer2_space = getattr(self, layer2 + "_space")
pitch = max_contact + max(layer1_space, layer2_space)
if self.get_preferred_direction(layer1) == "V":
contact_width = contact1.first_layer_height
else:
contact_width = contact1.first_layer_width
layer_space = getattr(self, layer1 + "_space")
return pitch
#print(layer_stack)
#print(contact1)
pitch = contact_width + layer_space
return round_to_grid(pitch)
def setup_drc_constants(self):
"""
These are some DRC constants used in many places

View File

@ -12,6 +12,7 @@ import debug
from math import sqrt
from tech import drc, GDS
from tech import layer as techlayer
from tech import layer_indices
from tech import layer_stacks
import os
from globals import OPTS
@ -47,11 +48,7 @@ class layout():
self.pwr_grid_layer = power_grid[0]
except ImportError:
self.pwr_grid_layer = "m3"
if "li" in techlayer:
self.layer_indices = ["active", "li", "m1", "m2", "m3", "m4"]
else:
self.layer_indices = ["active", "m1", "m2", "m3", "m4"]
############################################################
@ -451,7 +448,40 @@ class layout():
path=coordinates,
layer_widths=layer_widths)
def add_wire(self, layers, coordinates):
def add_zjog(self, layer, start, end, first_direction="H"):
"""
Add a simple jog at the halfway point.
If layer is a single value, it is a path.
If layer is a tuple, it is a wire with preferred directions.
"""
# vertical first
if first_direction == "V":
mid1 = vector(start.x, 0.5 * start.y + 0.5 * end.y)
mid2 = vector(end.x, mid1.y)
# horizontal first
elif first_direction == "H":
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
mid2 = vector(mid1, end.y)
else:
debug.error("Invalid direction for jog -- must be H or V.")
if layer in layer_stacks:
self.add_wire(layer, [start, mid1, mid2, end])
elif layer in techlayer:
self.add_path(layer, [start, mid1, mid2, end])
else:
debug.error("Could not find layer {}".format(layer))
def add_horizontal_zjog_path(self, layer, start, end):
""" Add a simple jog at the halfway point """
# horizontal first
mid1 = vector(0.5 * start.x + 0.5 * end.x, start.y)
mid2 = vector(mid1, end.y)
self.add_path(layer, [start, mid1, mid2, end])
def add_wire(self, layers, coordinates, widen_short_wires=True):
"""Connects a routing path on given layer,coordinates,width.
The layers are the (horizontal, via, vertical). """
import wire
@ -459,7 +489,8 @@ class layout():
# into rectangles and contacts
wire.wire(obj=self,
layer_stack=layers,
position_list=coordinates)
position_list=coordinates,
widen_short_wires=widen_short_wires)
def get_preferred_direction(self, layer):
""" Return the preferred routing directions """
@ -468,16 +499,6 @@ class layout():
def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """
# Non-preferred directions
if directions == "nonpref":
directions = (self.get_preferred_direction(layers[2]),
self.get_preferred_direction(layers[0]))
# Preferred if not specified
elif not directions or directions == "pref":
directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2]))
from sram_factory import factory
via = factory.create(module_type="contact",
layer_stack=layers,
@ -498,16 +519,6 @@ class layout():
Add a three layer via structure by the center coordinate
accounting for mirroring and rotation.
"""
# Non-preferred directions
if directions == "nonpref":
directions = (self.get_preferred_direction(layers[2]),
self.get_preferred_direction(layers[0]))
# Preferred if not specified
elif not directions or directions == "pref":
directions = (self.get_preferred_direction(layers[0]),
self.get_preferred_direction(layers[2]))
from sram_factory import factory
via = factory.create(module_type="contact",
layer_stack=layers,
@ -547,13 +558,6 @@ class layout():
implant_type=implant_type,
well_type=well_type)
def get_layer_index(self, layer_name):
"""
Return a layer index from bottom up in this tech.
"""
return self.layer_indices.index(layer_name)
def add_via_stack_center(self,
offset,
from_layer,
@ -586,8 +590,8 @@ class layout():
if from_layer == to_layer:
return last_via
from_id = self.get_layer_index(from_layer)
to_id = self.get_layer_index(to_layer)
from_id = layer_indices[from_layer]
to_id = layer_indices[to_layer]
if from_id < to_id: # grow the stack up
search_id = 0
@ -608,24 +612,13 @@ class layout():
implant_type=implant_type,
well_type=well_type)
if from_layer == "active":
via = self.__add_via_stack_internal(offset=offset,
directions=directions,
from_layer=curr_stack[next_id],
to_layer=to_layer,
via_func=via_func,
last_via=via,
size=size,
implant_type=implant_type,
well_type=well_type)
else:
via = self.__add_via_stack_internal(offset=offset,
directions=directions,
from_layer=curr_stack[next_id],
to_layer=to_layer,
via_func=via_func,
last_via=via,
size=size)
via = self.__add_via_stack_internal(offset=offset,
directions=directions,
from_layer=curr_stack[next_id],
to_layer=to_layer,
via_func=via_func,
last_via=via,
size=size)
return via
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
@ -781,47 +774,47 @@ class layout():
return blockages
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
def create_horizontal_pin_bus(self, layer, offset, names, length, pitch=None):
""" Create a horizontal bus of pins. """
return self.create_bus(layer,
pitch,
offset,
names,
length,
vertical=False,
make_pins=True)
make_pins=True,
pitch=pitch)
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
def create_vertical_pin_bus(self, layer, offset, names, length, pitch=None):
""" Create a horizontal bus of pins. """
return self.create_bus(layer,
pitch,
offset,
names,
length,
vertical=True,
make_pins=True)
make_pins=True,
pitch=pitch)
def create_vertical_bus(self, layer, pitch, offset, names, length):
def create_vertical_bus(self, layer, offset, names, length, pitch=None):
""" Create a horizontal bus. """
return self.create_bus(layer,
pitch,
offset,
names,
length,
vertical=True,
make_pins=False)
make_pins=False,
pitch=pitch)
def create_horizontal_bus(self, layer, pitch, offset, names, length):
def create_horizontal_bus(self, layer, offset, names, length, pitch=None):
""" Create a horizontal bus. """
return self.create_bus(layer,
pitch,
offset,
names,
length,
vertical=False,
make_pins=False)
make_pins=False,
pitch=pitch)
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
def create_bus(self, layer, offset, names, length, vertical, make_pins, pitch=None):
"""
Create a horizontal or vertical bus. It can be either just rectangles, or actual
layout pins. It returns an map of line center line positions indexed by name.
@ -831,11 +824,14 @@ class layout():
# half minwidth so we can return the center line offsets
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
if not pitch:
pitch = getattr(self, "{}_pitch".format(layer))
line_positions = {}
if vertical:
for i in range(len(names)):
line_offset = offset + vector(i * pitch, 0)
line_offset = offset + vector(i * pitch,
0)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
@ -900,8 +896,10 @@ class layout():
# left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y)
# Don't widen short wires because pin_pos and mid_pos could be really close
self.add_wire(layer_stack,
[bus_pos, mid_pos, pin_pos])
[bus_pos, mid_pos, pin_pos],
widen_short_wires=False)
# Connect to the pin on the instances with a via if it is
# not on the right layer
@ -918,33 +916,76 @@ class layout():
offset=bus_pos,
rotate=90)
def connect_vbus(self, src_pin, dest_pin, hlayer="m3", vlayer="m2"):
"""
Helper routine to connect an instance to a vertical bus.
Routes horizontal then vertical L shape.
"""
if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc()
else:
in_pos = src_pin.lc()
if src_pin.cy() < dest_pin.cy():
out_pos = dest_pin.bc()
else:
out_pos = dest_pin.uc()
# move horizontal first on layer stack
mid_pos = vector(out_pos.x, in_pos.y)
self.add_via_stack_center(from_layer=src_pin.layer,
to_layer=hlayer,
offset=in_pos)
self.add_path(hlayer, [in_pos, mid_pos])
self.add_via_stack_center(from_layer=hlayer,
to_layer=vlayer,
offset=mid_pos)
self.add_path(vlayer, [mid_pos, out_pos])
self.add_via_stack_center(from_layer=vlayer,
to_layer=dest_pin.layer,
offset=out_pos)
def connect_hbus(self, src_pin, dest_pin, hlayer="m3", vlayer="m2"):
"""
Helper routine to connect an instance to a horizontal bus.
Routes horizontal then vertical L shape.
"""
if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc()
else:
in_pos = src_pin.lc()
if src_pin.cy() < dest_pin.cy():
out_pos = dest_pin.lc()
else:
out_pos = dest_pin.rc()
# move horizontal first
mid_pos = vector(out_pos.x, in_pos.y)
self.add_via_stack_center(from_layer=src_pin.layer,
to_layer=hlayer,
offset=in_pos)
self.add_path(hlayer, [in_pos, mid_pos])
self.add_via_stack_center(from_layer=hlayer,
to_layer=vlayer,
offset=mid_pos)
self.add_path(vlayer, [mid_pos, out_pos])
self.add_via_stack_center(from_layer=vlayer,
to_layer=dest_pin.layer,
offset=out_pos)
def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """
if layer == "m1":
return (self.m1_pitch,
self.m1_pitch - self.m1_space,
self.m1_space)
elif layer == "m2":
return (self.m2_pitch,
self.m2_pitch - self.m2_space,
self.m2_space)
elif layer == "m3":
return (self.m3_pitch,
self.m3_pitch - self.m3_space,
self.m3_space)
elif layer == "m4":
from tech import layer as tech_layer
if "m4" in tech_layer:
return (self.m3_pitch,
self.m3_pitch - self.m4_space,
self.m4_space)
else:
return (self.m3_pitch,
self.m3_pitch - self.m3_space,
self.m3_space)
else:
debug.error("Cannot find layer pitch.")
try:
# FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
# It should just result in inefficient channel width but will work.
pitch = getattr(self, "{}_pitch".format(layer))
nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
space = getattr(self, "{}_space".format(layer))
except AttributeError:
debug.error("Cannot find layer pitch.", -1)
return (nonpref_pitch, pitch, pitch - space, space)
def add_horizontal_trunk_route(self,
pins,
trunk_offset,
@ -958,7 +999,7 @@ class layout():
min_x = min([pin.center().x for pin in pins])
# if we are less than a pitch, just create a non-preferred layer jog
if max_x-min_x <= pitch:
if max_x - min_x <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the vertical layer!
@ -1027,14 +1068,15 @@ class layout():
def create_channel_route(self, netlist,
offset,
layer_stack,
layer_dirs=None,
directions=None,
vertical=False):
"""
The net list is a list of the nets. Each net is a list of pins
to be connected. Offset is the lower-left of where the
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
routing channel will start. This does NOT try to minimize the
number of tracks -- instead, it picks an order to avoid the
vertical conflicts between pins.
vertical conflicts between pins. The track size must be the number of
nets times the *nonpreferred* routing of the non-track layer pitch.
"""
def remove_net_from_graph(pin, g):
@ -1051,12 +1093,17 @@ class layout():
g[other_pin]=conflicts
return g
def vcg_nets_overlap(net1, net2, vertical, pitch):
def vcg_nets_overlap(net1, net2, vertical):
"""
Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps.
"""
if vertical:
pitch = self.horizontal_nonpref_pitch
else:
pitch = self.vertical_nonpref_pitch
for pin1 in net1:
for pin2 in net2:
if vcg_pin_overlap(pin1, pin2, vertical, pitch):
@ -1069,8 +1116,7 @@ class layout():
# FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin,
# for example, so the
# extra comparison *shouldn't* matter.
# for example, so the extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch
@ -1080,7 +1126,7 @@ class layout():
overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
return overlaps
if not layer_dirs:
if not directions:
# Use the preferred layer directions
if self.get_preferred_direction(layer_stack[0]) == "V":
self.vertical_layer = layer_stack[0]
@ -1091,8 +1137,8 @@ class layout():
else:
# Use the layer directions specified to the router rather than
# the preferred directions
debug.check(layer_dirs[0] != layer_dirs[1], "Must have unique layer directions.")
if layer_dirs[0] == "V":
debug.check(directions[0] != directions[1], "Must have unique layer directions.")
if directions[0] == "V":
self.vertical_layer = layer_stack[0]
self.horizontal_layer = layer_stack[2]
else:
@ -1100,9 +1146,10 @@ class layout():
self.vertical_layer = layer_stack[2]
layer_stuff = self.get_layer_pitch(self.vertical_layer)
(self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
(self.vertical_nonpref_pitch, self.vertical_pitch, self.vertical_width, self.vertical_space) = layer_stuff
layer_stuff = self.get_layer_pitch(self.horizontal_layer)
(self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
(self.horizontal_nonpref_pitch, self.horizontal_pitch, self.horizontal_width, self.horizontal_space) = layer_stuff
# FIXME: Must extend this to a horizontal conflict graph
# too if we want to minimize the
@ -1122,6 +1169,10 @@ class layout():
index += 1
nets[net_name] = pin_list
# print("Nets:")
# for net_name in nets:
# print(net_name, [x.name for x in nets[net_name]])
# Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now
for net_name1 in nets:
@ -1133,21 +1184,15 @@ class layout():
# Skip yourself
if net_name1 == net_name2:
continue
if vertical and vcg_nets_overlap(nets[net_name1],
nets[net_name2],
vertical,
self.vertical_pitch):
vcg[net_name2].append(net_name1)
elif not vertical and vcg_nets_overlap(nets[net_name1],
nets[net_name2],
vertical,
self.horizontal_pitch):
if vcg_nets_overlap(nets[net_name1],
nets[net_name2],
vertical):
vcg[net_name2].append(net_name1)
# list of routes to do
while vcg:
# from pprint import pformat
# print("VCG:\n",pformat(vcg))
# print("VCG:\n", pformat(vcg))
# get a route from conflict graph with empty fanout set
net_name = None
for net_name, conflicts in vcg.items():
@ -1171,26 +1216,28 @@ class layout():
self.add_vertical_trunk_route(pin_list,
offset,
layer_stack,
self.vertical_pitch)
offset += vector(self.vertical_pitch, 0)
self.vertical_nonpref_pitch)
# This accounts for the via-to-via spacings
offset += vector(self.horizontal_nonpref_pitch, 0)
else:
self.add_horizontal_trunk_route(pin_list,
offset,
layer_stack,
self.horizontal_pitch)
offset += vector(0, self.horizontal_pitch)
self.horizontal_nonpref_pitch)
# This accounts for the via-to-via spacings
offset += vector(0, self.vertical_nonpref_pitch)
def create_vertical_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
def create_vertical_channel_route(self, netlist, offset, layer_stack, directions=None):
"""
Wrapper to create a vertical channel route
"""
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=True)
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=True)
def create_horizontal_channel_route(self, netlist, offset, layer_stack, layer_dirs=None):
def create_horizontal_channel_route(self, netlist, offset, layer_stack, directions=None):
"""
Wrapper to create a horizontal channel route
"""
self.create_channel_route(netlist, offset, layer_stack, layer_dirs, vertical=False)
self.create_channel_route(netlist, offset, layer_stack, directions, vertical=False)
def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """

View File

@ -19,11 +19,14 @@ class wire(wire_path):
not, it will always go down first.
The points are the center of the wire.
The layer stack is the vertical, contact/via, and horizontal layers, respectively.
The widen option will avoid via-to-via spacing problems for really short segments
(added as an option so we can disable it in bus connections)
"""
def __init__(self, obj, layer_stack, position_list):
def __init__(self, obj, layer_stack, position_list, widen_short_wires=True):
self.obj = obj
self.layer_stack = layer_stack
self.position_list = position_list
self.widen_short_wires = widen_short_wires
self.pins = [] # used for matching parm lengths
self.switch_pos_list = []
@ -114,7 +117,7 @@ class wire(wire_path):
line_length = pl[index + 1][0] - pl[index][0]
# Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via
if abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.horiz_layer_contact_width:
width = self.horiz_layer_contact_width
else:
width = self.horiz_layer_width
@ -134,7 +137,7 @@ class wire(wire_path):
line_length = pl[index + 1][1] - pl[index][1]
# Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via
if abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
if self.widen_short_wires and abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
width = self.vert_layer_contact_width
else:
width = self.vert_layer_width

View File

@ -24,11 +24,12 @@ def create_rectilinear_route(my_list):
my_list.append(vector(pl[index][0], pl[index + 1][1]))
my_list.append(vector(pl[-1]))
return my_list
class wire_path():
"""
Object metal wire_path; given the layer type
Add a wire_path of minimium metal width between a set of points.
Add a wire_path of minimium metal width between a set of points.
The points should be rectilinear to control the bend points. If
not, it will always go down first. The points are the center of the wire_path.
If width is not given, it uses minimum layer width.
@ -37,7 +38,7 @@ class wire_path():
self.obj = obj
self.layer_name = layer
self.layer_id = techlayer[layer]
if width==None:
if width == None:
self.layer_width = drc["minwidth_{0}".format(layer)]
else:
self.layer_width = width
@ -46,7 +47,6 @@ class wire_path():
self.switch_pos_list = []
self.create_layout()
def create_layout(self):
self.create_rectilinear()
self.connect_corner()
@ -60,9 +60,9 @@ class wire_path():
def connect_corner(self):
""" Add a corner square at every corner of the wire_path."""
from itertools import tee,islice
nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n))))
threewise=nwise(self.position_list,3)
from itertools import tee, islice
nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
threewise=nwise(self.position_list, 3)
for (a, offset, c) in list(threewise):
# add a exceptions to prevent a corner when we retrace back in the same direction
@ -74,7 +74,6 @@ class wire_path():
offset[1] - 0.5 * self.layer_width]
self.draw_corner_wire(corner_offset)
def draw_corner_wire(self, offset):
""" This function adds the corner squares since the center
line convention only draws to the center of the corner."""
@ -117,7 +116,7 @@ class wire_path():
def add_line(self, layer_name, length, offset, orientation, layer_width):
"""
straight line object with layer_minwidth
straight line object with layer_minwidth
(orientation: "vertical" or "horizontal") default is vertical
"""

View File

@ -50,7 +50,7 @@ class bitcell(bitcell_base.bitcell_base):
self.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
if props.bitcell.split_wl:

View File

@ -264,7 +264,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ 2 * self.implant_enclose_active \
+ 0.5*(self.inverter_pmos.active_contact.height - self.m1_width)
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space
self.vdd_offset = max(implant_constraint, metal1_constraint) + 0.5*self.m1_width
self.vdd_offset = max(implant_constraint, metal1_constraint) + self.m1_width
# read port dimensions
width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx()
@ -275,7 +275,7 @@ class pbitcell(bitcell_base.bitcell_base):
Calculate positions that describe the edges
and dimensions of the cell
"""
self.botmost_ypos = self.m1_offset - self.total_ports * self.m1_pitch
self.botmost_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
self.topmost_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \
+ self.inverter_gap \
@ -378,14 +378,15 @@ class pbitcell(bitcell_base.bitcell_base):
+ 0.5 * contact.poly_contact.height,
self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset_left)
offset=contact_offset_left,
directions=("H", "H"))
contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly_contact.height,
self.cross_couple_lower_ypos)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset_right)
offset=contact_offset_right,
directions=("H", "H"))
# connect contacts to gate poly (cross couple connections)
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x,
@ -399,12 +400,12 @@ class pbitcell(bitcell_base.bitcell_base):
def route_rails(self):
""" Adds gnd and vdd rails and connects them to the inverters """
# Add rails for vdd and gnd
gnd_ypos = self.m1_offset - self.total_ports * self.m1_pitch
gnd_ypos = self.m1_offset - self.total_ports * self.m1_nonpref_pitch
self.gnd_position = vector(0, gnd_ypos)
self.add_rect_center(layer="m1",
offset=self.gnd_position,
width=self.width)
self.add_power_pin("gnd", vector(0, gnd_ypos))
self.add_power_pin("gnd", vector(0, gnd_ypos), directions=("H", "H"))
vdd_ypos = self.inverter_nmos_ypos \
@ -416,7 +417,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.add_rect_center(layer="m1",
offset=self.vdd_position,
width=self.width)
self.add_power_pin("vdd", vector(0, vdd_ypos))
self.add_power_pin("vdd", vector(0, vdd_ypos), directions=("H", "H"))
def create_readwrite_ports(self):
"""
@ -483,7 +484,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.port_ypos])
# add pin for RWWL
rwwl_ypos = self.m1_offset - k * self.m1_pitch
rwwl_ypos = self.m1_offset - k * self.m1_nonpref_pitch
self.rwwl_positions[k] = vector(0, rwwl_ypos)
self.add_layout_pin_rect_center(text=self.rw_wl_names[k],
layer="m1",
@ -579,8 +580,8 @@ class pbitcell(bitcell_base.bitcell_base):
# add pin for WWL
wwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_rw_ports * self.m1_pitch \
- k * self.m1_pitch
- self.num_rw_ports * self.m1_nonpref_pitch \
- k * self.m1_nonpref_pitch
self.wwl_positions[k] = vector(0, wwl_ypos)
self.add_layout_pin_rect_center(text=self.w_wl_names[k],
layer="m1",
@ -705,9 +706,9 @@ class pbitcell(bitcell_base.bitcell_base):
# add pin for RWL
rwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_rw_ports * self.m1_pitch \
- self.num_w_ports * self.m1_pitch \
- k * self.m1_pitch
- self.num_rw_ports * self.m1_nonpref_pitch \
- self.num_w_ports * self.m1_nonpref_pitch \
- k * self.m1_nonpref_pitch
self.rwl_positions[k] = vector(0, rwl_ypos)
self.add_layout_pin_rect_center(text=self.r_wl_names[k],
layer="m1",
@ -771,6 +772,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("m1",
[port_contact_offset, wl_contact_offset])
@ -821,7 +823,8 @@ class pbitcell(bitcell_base.bitcell_base):
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest)
offset=port_contact_offest,
directions="nonpref")
self.add_path("m2",
[port_contact_offest, bl_offset], width=contact.m1_via.height)
@ -833,7 +836,8 @@ class pbitcell(bitcell_base.bitcell_base):
# Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell:
self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest)
offset=port_contact_offest,
directions="nonpref")
self.add_path("m2",
[port_contact_offest, br_offset], width=contact.m1_via.height)
@ -850,7 +854,9 @@ class pbitcell(bitcell_base.bitcell_base):
for position in nmos_contact_positions:
self.add_via_center(layers=self.m1_stack,
offset=position)
offset=position,
directions=("V", "V"))
if position.x > 0:
contact_correct = 0.5 * contact.m1_via.height
@ -859,7 +865,8 @@ class pbitcell(bitcell_base.bitcell_base):
supply_offset = vector(position.x + contact_correct,
self.gnd_position.y)
self.add_via_center(layers=self.m1_stack,
offset=supply_offset)
offset=supply_offset,
directions=("H", "H"))
self.add_path("m2", [position, supply_offset])
@ -924,13 +931,16 @@ class pbitcell(bitcell_base.bitcell_base):
- self.poly_to_contact - 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack,
offset=left_storage_contact)
offset=left_storage_contact,
directions=("H", "H"))
right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
+ self.poly_to_contact + 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack,
offset=right_storage_contact)
offset=right_storage_contact,
directions=("H", "H"))
inverter_gate_offset_left = vector(self.inverter_nmos_left.get_pin("G").lc().x, self.cross_couple_upper_ypos)
self.add_path("poly", [left_storage_contact, inverter_gate_offset_left])
@ -1007,8 +1017,7 @@ class pbitcell(bitcell_base.bitcell_base):
well_height = self.vdd_position.y - inverter_well_ypos \
+ self.nwell_enclose_active + drc["minwidth_tx"]
# FIXME fudge factor xpos
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
offset = [inverter_well_xpos, inverter_well_ypos]
self.add_rect(layer="nwell",
offset=offset,
width=well_width,

View File

@ -83,10 +83,10 @@ class bank(design.design):
self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
for port in self.write_ports:
for bit in range(self.word_size):
self.add_pin("din{0}_{1}".format(port,bit), "INPUT")
self.add_pin("din{0}_{1}".format(port, bit), "INPUT")
for port in self.all_ports:
for bit in range(self.addr_size):
self.add_pin("addr{0}_{1}".format(port,bit), "INPUT")
self.add_pin("addr{0}_{1}".format(port, bit), "INPUT")
# For more than one bank, we have a bank select and name
# the signals gated_*.
@ -128,16 +128,14 @@ class bank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
# This will ensure the pin is only on the top or bottom edge
if port % 2:
via_offset = bl_pin.uc() + vector(0, self.m2_pitch)
via_offset = bl_pin.uc() + vector(0, 1.5 * self.m2_pitch)
left_right_offset = vector(self.max_x_offset, via_offset.y)
else:
via_offset = bl_pin.bc() - vector(0, self.m2_pitch)
via_offset = bl_pin.bc() - vector(0, 1.5 * self.m2_pitch)
left_right_offset = vector(self.min_x_offset, via_offset.y)
if bl_pin == "m1":
self.add_via_center(layers=self.m1_stack,
offset=via_offset)
self.add_via_center(layers=self.m2_stack,
offset=via_offset)
self.add_via_stack_center(from_layer=bl_pin.layer,
to_layer="m3",
offset=via_offset)
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
layer="m3",
start=left_right_offset,
@ -635,11 +633,10 @@ class bank(design.design):
# Port 0
# The bank is at (0,0), so this is to the left of the y-axis.
# 2 pitches on the right for vias/jogs to access the inputs
control_bus_offset = vector(-self.m2_pitch * self.num_control_lines[0] - self.m2_pitch, self.min_y_offset)
control_bus_offset = vector(-self.m3_pitch * self.num_control_lines[0] - self.m3_pitch, self.min_y_offset)
# The control bus is routed up to two pitches below the bitcell array
control_bus_length = self.main_bitcell_array_bottom - self.min_y_offset - 2 * self.m1_pitch
self.bus_xoffset[0] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=self.control_signals[0],
length=control_bus_length,
@ -650,11 +647,10 @@ class bank(design.design):
if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array
control_bus_length = self.max_y_offset - self.main_bitcell_array_top - 2 * self.m1_pitch
control_bus_offset = vector(self.bitcell_array_right + self.m2_pitch,
control_bus_offset = vector(self.bitcell_array_right + self.m3_pitch,
self.max_y_offset - control_bus_length)
# The bus for the right port is reversed so that the rbl_wl is closest to the array
self.bus_xoffset[1] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset,
names=list(reversed(self.control_signals[1])),
length=control_bus_length,
@ -844,9 +840,9 @@ class bank(design.design):
self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port % 2:
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_pitch, 0)
offset = self.column_decoder_inst[port].ll() - vector(self.num_col_addr_lines * self.m2_nonpref_pitch, 0)
else:
offset = self.column_decoder_inst[port].lr() + vector(self.m2_pitch, 0)
offset = self.column_decoder_inst[port].lr() + vector(self.m2_nonpref_pitch, 0)
decode_pins = [self.column_decoder_inst[port].get_pin(x) for x in decode_names]
@ -854,7 +850,9 @@ class bank(design.design):
column_mux_pins = [self.port_data_inst[port].get_pin(x) for x in sel_names]
route_map = list(zip(decode_pins, column_mux_pins))
self.create_vertical_channel_route(route_map, offset, self.m1_stack)
self.create_vertical_channel_route(route_map,
offset,
self.m1_stack)
def add_lvs_correspondence_points(self):
"""
@ -910,30 +908,39 @@ class bank(design.design):
# pre-decoder and this connection is in metal3
connection = []
connection.append((self.prefix + "p_en_bar{}".format(port),
self.port_data_inst[port].get_pin("p_en_bar").lc()))
self.port_data_inst[port].get_pin("p_en_bar").lc(),
self.port_data_inst[port].get_pin("p_en_bar").layer))
rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
connection.append((self.prefix + "wl_en{}".format(port),
self.bitcell_array_inst.get_pin(rbl_wl_name).lc()))
self.bitcell_array_inst.get_pin(rbl_wl_name).lc(),
self.bitcell_array_inst.get_pin(rbl_wl_name).layer))
if port in self.write_ports:
if port % 2:
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en").rc()))
self.port_data_inst[port].get_pin("w_en").rc(),
self.port_data_inst[port].get_pin("w_en").layer))
else:
connection.append((self.prefix + "w_en{}".format(port),
self.port_data_inst[port].get_pin("w_en").lc()))
self.port_data_inst[port].get_pin("w_en").lc(),
self.port_data_inst[port].get_pin("w_en").layer))
if port in self.read_ports:
connection.append((self.prefix + "s_en{}".format(port),
self.port_data_inst[port].get_pin("s_en").lc()))
self.port_data_inst[port].get_pin("s_en").lc(),
self.port_data_inst[port].get_pin("s_en").layer))
for (control_signal, pin_pos) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal]
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y)
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=control_pos)
for (control_signal, pin_pos, pin_layer) in connection:
if port==0:
y_offset = self.min_y_offset
else:
y_offset = self.max_y_offset
control_pos = vector(self.bus_xoffset[port][control_signal].x, y_offset)
if pin_layer == "m1":
self.add_wire(self.m1_stack, [control_pos, pin_pos])
elif pin_layer == "m3":
self.add_wire(self.m2_stack[::-1], [control_pos, pin_pos])
# clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port)

View File

@ -205,7 +205,7 @@ class bank_select(design.design):
bank_sel_line_end = vector(xoffset_bank_sel, self.yoffset_maxpoint)
self.add_path("m2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=self.m1_stack,
offset=bank_sel_inv_pin.lc())
offset=bank_sel_inv_pin.center())
# Route the pin to the left edge as well
bank_sel_pin_pos=vector(0, 0)
@ -242,30 +242,31 @@ class bank_select(design.design):
# Connect the logic output to inverter input
out_pin = logic_inst.get_pin("Z")
out_pos = out_pin.rc()
out_pos = out_pin.center()
in_pin = inv_inst.get_pin("A")
in_pos = in_pin.lc()
in_pos = in_pin.center()
mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar
logic_pos = logic_inst.get_pin("B").lc() - vector(0.5 * contact.m1_via.height, 0)
logic_pin = logic_inst.get_pin("B")
logic_pos = logic_pin.center()
input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("m2", [logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H", "H"))
self.add_path("m3", [logic_pos, input_pos])
self.add_via_center(self.m2_stack,
input_pos)
self.add_via_stack_center(from_layer=logic_pin.layer,
to_layer="m3",
offset=logic_pos)
# Connect the logic A input to the input pin
logic_pos = logic_inst.get_pin("A").lc()
logic_pin = logic_inst.get_pin("A")
logic_pos = logic_pin.center()
input_pos = vector(0, logic_pos.y)
self.add_via_center(layers=self.m1_stack,
offset=logic_pos,
directions=("H", "H"))
self.add_via_center(layers=self.m2_stack,
offset=logic_pos,
directions=("H", "H"))
self.add_via_stack_center(from_layer=logic_pin.layer,
to_layer="m3",
offset=logic_pos)
self.add_layout_pin_segment_center(text=input_name,
layer="m3",
start=input_pos,

View File

@ -384,7 +384,10 @@ class control_logic(design.design):
height = self.control_logic_center.y - self.m2_pitch
offset = vector(self.ctrl_dff_array.width, 0)
self.rail_offsets = self.create_vertical_bus("m2", self.m2_pitch, offset, self.internal_bus_list, height)
self.rail_offsets = self.create_vertical_bus("m2",
offset,
self.internal_bus_list,
height)
def create_instances(self):
""" Create all the instances """
@ -432,7 +435,7 @@ class control_logic(design.design):
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row)
row += 1
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row)
row += 1
@ -522,16 +525,8 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack,
offset=clk_pos)
# Connect this at the bottom of the buffer
out_pos = self.clk_buf_inst.get_pin("Z").center()
mid1 = vector(out_pos.x, 2 * self.m2_pitch)
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
bus_pos = self.rail_offsets["clk_buf"]
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.clk_buf_inst.get_pin("Z").center())
self.route_output_to_bus_jogged(self.clk_buf_inst,
"clk_buf")
self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self):
@ -541,7 +536,7 @@ class control_logic(design.design):
self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2)
self.connect_inst(["cs", "clk_bar", "gated_clk_bar", "vdd", "gnd"])
self.connect_inst(["clk_bar", "cs", "gated_clk_bar", "vdd", "gnd"])
def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset
@ -554,31 +549,26 @@ class control_logic(design.design):
def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x, out_pos.y)
self.add_path("m1", [out_pos, mid1, in_pos])
in_pos = self.gated_clk_bar_inst.get_pin("A").center()
self.add_zjog("m1", out_pos, in_pos)
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["A"], ["cs"])
clkbuf_map = zip(["B"], ["cs"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("A").center())
b_pin = self.gated_clk_bar_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m3",
offset=b_pin.center())
# This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("Z").center())
self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
"gated_clk_bar")
def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf",
@ -594,7 +584,9 @@ class control_logic(design.design):
def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_buf_inst, self.rail_offsets)
self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.rail_offsets)
clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map,
@ -602,8 +594,10 @@ class control_logic(design.design):
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_buf_inst.get_pin("Z").center())
z_pin = self.gated_clk_buf_inst.get_pin("Z")
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_wlen_row(self):
# input pre_p_en, output: wl_en
@ -647,11 +641,16 @@ class control_logic(design.design):
in_map = zip(["A", "B"], ["gated_clk_buf", "rbl_bl_delay"])
self.connect_vertical_bus(in_map, self.p_en_bar_nand_inst, self.rail_offsets)
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
out_pin = self.p_en_bar_nand_inst.get_pin("Z")
out_pos = out_pin.center()
in_pin = self.p_en_bar_driver_inst.get_pin("A")
in_pos = in_pin.center()
mid1 = vector(in_pos.x, out_pos.y)
self.add_path(out_pin.layer, [out_pos, mid1, in_pos])
self.add_via_stack_center(from_layer=out_pin.layer,
to_layer=in_pin.layer,
offset=in_pin.center())
self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
def create_sen_row(self):
@ -704,11 +703,7 @@ class control_logic(design.design):
# Connect from delay line
# Connect to rail
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets, ("m3", "via2", "m2"))
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center())
self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "rbl_bl_delay_bar")
rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
@ -766,11 +761,12 @@ class control_logic(design.design):
dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else:
dff_out_map = zip(["dout_bar_0"], ["cs"])
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, ("m3", "via2", "m2"))
self.connect_vertical_bus(dff_out_map, self.ctrl_dff_inst, self.rail_offsets, self.m2_stack[::-1])
# Connect the clock rail to the other clock rail
# by routing in the supply rail track to avoid channel conflicts
in_pos = self.ctrl_dff_inst.get_pin("clk").uc()
mid_pos = in_pos + vector(0, 2 * self.m2_pitch)
mid_pos = in_pos + vector(0, self.and2.height)
rail_pos = vector(self.rail_offsets["clk_buf"].x, mid_pos.y)
self.add_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack,
@ -823,10 +819,10 @@ class control_logic(design.design):
self.add_path("m1", [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong.
@ -1000,3 +996,15 @@ class control_logic(design.design):
offset = vector(x_offset, y_offset)
inst.place(offset, mirror)
return x_offset + inst.width
def route_output_to_bus_jogged(self, inst, name):
# Connect this at the bottom of the buffer
out_pos = inst.get_pin("Z").center()
mid1 = vector(out_pos.x, out_pos.y - 0.25 * inst.mod.height)
mid2 = vector(self.rail_offsets[name].x, mid1.y)
bus_pos = self.rail_offsets[name]
self.add_wire(self.m2_stack[::-1], [out_pos, mid1, mid2, bus_pos])
# The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack,
offset=out_pos)

View File

@ -180,7 +180,7 @@ class dff_buf(design.design):
height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z")
mid_pos = dout_pin.center() + vector(self.m1_pitch, 0)
mid_pos = dout_pin.center() + vector(self.m1_nonpref_pitch, 0)
q_pos = mid_pos - vector(0, self.m2_pitch)
self.add_layout_pin_rect_center(text="Q",
layer="m2",

View File

@ -12,7 +12,7 @@ from sram_factory import factory
from vector import vector
from globals import OPTS
from errors import drc_error
from tech import cell_properties
from tech import cell_properties, layer
class hierarchical_decoder(design.design):
@ -89,6 +89,7 @@ class hierarchical_decoder(design.design):
self.place_pre_decoder()
self.place_row_decoder()
self.route_inputs()
self.route_outputs()
self.route_decoder_bus()
self.route_vdd_gnd()
self.offset_all_coordinates()
@ -103,7 +104,7 @@ class hierarchical_decoder(design.design):
height=self.cell_height)
self.add_mod(self.and2)
self.and3 = factory.create(module_type="pand3",
height=self.cell_height)
height=self.cell_height)
self.add_mod(self.and3)
self.add_decoders()
@ -186,6 +187,13 @@ class hierarchical_decoder(design.design):
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
# We will need to use M2 and M3 in the vertical bus if we have multiple decoders per row
if self.decoders_per_row == 1:
self.decoder_bus_pitch = self.m2_pitch
elif self.decoders_per_row == 2:
self.decoder_bus_pitch = self.m3_pitch
else:
debug.error("Insufficient layers for multi-bit height decoder.", -1)
# Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5):
@ -194,20 +202,21 @@ class hierarchical_decoder(design.design):
else:
nand_width = self.and3.width
nand_inputs = 3
self.internal_routing_width = self.m3_pitch * (self.total_number_of_predecoder_outputs + 1)
self.internal_routing_width = self.decoder_bus_pitch * (self.total_number_of_predecoder_outputs + 1)
self.row_decoder_height = self.inv.height * self.num_rows
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m3_pitch
#print(self.decoders_per_row, nand_inputs)
#print(decoder_input_wire_height, self.cell_height)
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
# print(self.decoders_per_row, nand_inputs)
# print(decoder_input_wire_height, self.cell_height)
if decoder_input_wire_height > self.cell_height:
debug.warning("Cannot fit multi-bit decoder routes per row.")
#debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch
self.input_routing_width = (self.num_inputs + 1) * self.m3_pitch
# Calculates height and width of hierarchical decoder
# Add extra pitch for good measure
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m2_pitch
self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width \
+ self.decoders_per_row * nand_width + self.inv.width
@ -226,8 +235,7 @@ class hierarchical_decoder(design.design):
input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_bus = self.create_vertical_pin_bus(layer="m3",
pitch=self.m3_pitch,
self.input_bus = self.create_vertical_pin_bus(layer="m2",
offset=input_offset,
names=input_bus_names,
length=input_height)
@ -276,13 +284,11 @@ class hierarchical_decoder(design.design):
self.add_via_stack_center(from_layer="m2",
to_layer="m3",
offset=input_offset,
directions="nonpref")
offset=input_offset)
self.add_via_stack_center(from_layer="m2",
to_layer="m3",
offset=output_offset,
directions="nonpref")
self.add_path(("m2"), [input_offset, output_offset])
offset=output_offset)
self.add_path("m3", [input_offset, output_offset])
def add_pins(self):
""" Add the module pins """
@ -424,7 +430,7 @@ class hierarchical_decoder(design.design):
"""
if (self.num_inputs >= 4):
self.place_decoder_and_array()
self.route_decoder()
def place_decoder_and_array(self):
"""
@ -460,16 +466,28 @@ class hierarchical_decoder(design.design):
self.and_inst[inst_index].place(offset=vector(x_off, y_off),
mirror=mirror)
def route_decoder(self):
def route_outputs(self):
""" Add the pins. """
for output in range(self.num_outputs):
z_pin = self.and_inst[output].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(output),
layer="m1",
offset=z_pin.ll(),
width=z_pin.width(),
height=z_pin.height())
max_xoffset = max(x.rx() for x in self.and_inst)
for output_index in range(self.num_outputs):
row_remainder = (output_index % self.decoders_per_row)
and_inst = self.and_inst[output_index]
z_pin = and_inst.get_pin("Z")
if row_remainder == 0 and self.decoders_per_row > 1:
layer = "m3"
self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m3",
offset=z_pin.center())
else:
layer = z_pin.layer
self.add_layout_pin_segment_center(text="decode_{0}".format(output_index),
layer=layer,
start=z_pin.center(),
end=vector(max_xoffset, z_pin.cy()))
def route_decoder_bus(self):
"""
@ -480,8 +498,8 @@ class hierarchical_decoder(design.design):
if (self.num_inputs >= 4):
# This leaves an offset for the predecoder output jogs
input_bus_names = ["predecode_{0}".format(i) for i in range(self.total_number_of_predecoder_outputs)]
self.predecode_bus = self.create_vertical_pin_bus(layer="m3",
pitch=self.m3_pitch,
self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.decoder_bus_pitch,
offset=vector(0, 0),
names=input_bus_names,
length=self.height)
@ -525,22 +543,27 @@ class hierarchical_decoder(design.design):
"""
output_index = 0
if "li" in layer:
self.decoder_layers = [self.m1_stack, self.m2_stack[::-1]]
else:
self.decoder_layers = [self.m2_stack[::-1]]
debug.check(self.decoders_per_row <= len(self.decoder_layers), "Must have more layers for multi-height decoder.")
if (self.num_inputs == 4 or self.num_inputs == 5):
for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (2 * row_remainder + 1) * self.m3_pitch
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
output_index,
0)
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
output_index,
1)
output_index = output_index + 1
elif (self.num_inputs > 5):
@ -549,21 +572,21 @@ class hierarchical_decoder(design.design):
for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus?
if (output_index < self.num_outputs):
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height + (3 * row_remainder + 1) * self.m2_pitch
predecode_name = "predecode_{}".format(index_A)
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
output_index,
0)
predecode_name = "predecode_{}".format(index_B)
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m2_pitch)
output_index,
1)
predecode_name = "predecode_{}".format(index_C)
self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("C"),
row_offset + 2 * self.m2_pitch)
output_index,
2)
output_index = output_index + 1
def route_vdd_gnd(self):
@ -583,47 +606,56 @@ class hierarchical_decoder(design.design):
# The nand and inv are the same height rows...
supply_pin = self.and_inst[num].get_pin(pin_name)
pin_pos = vector(xoffset, supply_pin.cy())
self.add_path("m1",
self.add_path(supply_pin.layer,
[supply_pin.lc(), vector(xoffset, supply_pin.cy())])
self.add_power_pin(name=pin_name,
loc=pin_pos)
loc=pin_pos,
start_layer=supply_pin.layer)
# Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst:
self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd")
def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
def route_predecode_bus_outputs(self, rail_name, pin, output_index, pin_index):
"""
Connect the routing rail to the given metal1 pin
using a routing track at the given y_offset
"""
pin_pos = pin.center()
# If we have a single decoder per row, we can route on M1
if self.decoders_per_row == 1:
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
self.add_path("m2", [rail_pos, pin_pos])
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2",
offset=pin_pos,
directions=("H", "H"))
self.add_via_stack_center(from_layer="m2",
to_layer="m3",
offset=rail_pos,
directions="nonpref")
# If not, we must route over the decoder cells on M3
else:
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.m2_stack, [rail_pos, mid_pos, pin_pos])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
self.add_via_stack_center(from_layer=pin_pos.layer,
to_layer="m3",
offset=pin_pos,
directions="nonpref")
row_index = math.floor(output_index / self.decoders_per_row)
row_remainder = (output_index % self.decoders_per_row)
row_offset = row_index * self.and_inst[0].height
pin_pos = pin.center()
# y_offset is the same for both the M2 and M4 routes so that the rail
# contacts align and don't cause problems
if pin_index == 0:
# Bottom pitch
y_offset = row_offset
elif pin_index == 1:
# One pitch from top
y_offset = row_offset + self.and_inst[0].height - self.m3_pitch
elif pin_index == 2:
# One pitch from bottom
y_offset = row_offset + self.m3_pitch
else:
debug.error("Invalid decoder pitch.")
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
self.add_via_stack_center(from_layer="m2",
to_layer=self.decoder_layers[row_remainder][0],
offset=rail_pos)
self.add_via_stack_center(from_layer=pin.layer,
to_layer=self.decoder_layers[row_remainder][2],
offset=pin_pos,
directions=("H", "H"))
def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
"""
Connect the routing rail to the given metal1 pin using a jog
@ -631,15 +663,21 @@ class hierarchical_decoder(design.design):
"""
# This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router.
# pin_pos = pin.center()
# mid_point1 = vector(x_offset, pin_pos.y)
# mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
# rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
# self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
pin_pos = pin.center()
mid_point1 = vector(x_offset, pin_pos.y)
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2)
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y)
self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos])
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
self.add_path("m1", [pin_pos, rail_pos])
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m1",
offset=pin_pos)
self.add_via_stack_center(from_layer="m1",
to_layer="m3",
offset=rail_pos,
directions="nonpref")
to_layer="m2",
offset=rail_pos)
def input_load(self):
if self.determine_predecodes(self.num_inputs)[1]==0:

View File

@ -11,6 +11,7 @@ import math
import contact
from vector import vector
from sram_factory import factory
from tech import cell_properties
class hierarchical_predecode(design.design):
@ -19,7 +20,17 @@ class hierarchical_predecode(design.design):
"""
def __init__(self, name, input_number, height=None):
self.number_of_inputs = input_number
self.cell_height = height
if not height:
b = factory.create(module_type="bitcell")
try:
self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
except AttributeError:
self.cell_multiple = 1
self.cell_height = self.cell_multiple * b.height
else:
self.cell_height = height
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name)
@ -57,10 +68,10 @@ class hierarchical_predecode(design.design):
self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters
self.x_off_inv_1 = self.number_of_inputs*self.m3_pitch
self.x_off_inv_1 = self.number_of_inputs * self.m2_pitch + self.m2_space
# x offset to AND decoder includes the left rails, mid rails and inverters, plus two extra m2 pitches
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2*self.number_of_inputs + 2) * self.m3_pitch
self.x_off_and = self.x_off_inv_1 + self.inv.width + (2 * self.number_of_inputs + 2) * self.m2_pitch
# x offset to output inverters
self.width = self.x_off_and + self.and_mod.width
@ -68,9 +79,8 @@ class hierarchical_predecode(design.design):
def route_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """
input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5 * self.m3_width, self.m1_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m3",
pitch=self.m3_pitch,
offset = vector(0.5 * self.m2_width, self.m3_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m2",
offset=offset,
names=input_names,
length=self.height - 2 * self.m1_pitch)
@ -78,9 +88,8 @@ class hierarchical_predecode(design.design):
invert_names = ["Abar_{}".format(x) for x in range(self.number_of_inputs)]
non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m3_pitch, self.m1_pitch)
self.decode_rails = self.create_vertical_bus(layer="m3",
pitch=self.m3_pitch,
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m2_pitch, self.m3_pitch)
self.decode_rails = self.create_vertical_bus(layer="m2",
offset=offset,
names=decode_names,
length=self.height - 2 * self.m1_pitch)
@ -151,11 +160,13 @@ class hierarchical_predecode(design.design):
a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x, y_offset)
a_pos = vector(self.decode_rails[a_pin].x, y_offset)
self.add_path("m2", [in_pos, a_pos])
self.add_via_center(layers=self.m2_stack,
offset=[self.input_rails[in_pin].x, y_offset])
self.add_via_center(layers=self.m2_stack,
offset=[self.decode_rails[a_pin].x, y_offset])
self.add_path("m1", [in_pos, a_pos])
self.add_via_stack_center(from_layer="m1",
to_layer="m2",
offset=[self.input_rails[in_pin].x, y_offset])
self.add_via_stack_center(from_layer="m1",
to_layer="m2",
offset=[self.decode_rails[a_pin].x, y_offset])
def route_output_and(self):
"""
@ -188,21 +199,20 @@ class hierarchical_predecode(design.design):
rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
self.add_via_stack_center(from_layer=inv_out_pin.layer,
to_layer="m3",
offset=rail_pos,
directions="nonpref")
to_layer="m2",
offset=rail_pos)
# route input
pin = self.in_inst[inv_num].get_pin("A")
inv_in_pos = pin.lc()
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y)
self.add_path("m2", [in_pos, inv_in_pos])
self.add_path("m1", [in_pos, inv_in_pos])
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m1",
offset=inv_in_pos)
self.add_via_stack_center(from_layer="m1",
to_layer="m2",
offset=inv_in_pos,
directions="nonpref")
self.add_via_center(layers=self.m2_stack,
offset=in_pos)
offset=in_pos)
def route_and_to_rails(self):
# This 2D array defines the connection mapping
@ -221,14 +231,19 @@ class hierarchical_predecode(design.design):
pin = self.and_inst[k].get_pin(gate_pin)
pin_pos = pin.center()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m2", [rail_pos, pin_pos])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos,
directions="nonpref")
self.add_via_stack_center(from_layer=pin.layer,
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_stack_center(from_layer="m1",
to_layer="m2",
offset=rail_pos)
if gate_pin == "A":
direction = None
else:
direction = ("H", "H")
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m1",
offset=pin_pos,
directions=("H", "H"))
directions=direction)
def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """
@ -243,14 +258,16 @@ class hierarchical_predecode(design.design):
for n in ["vdd", "gnd"]:
and_pin = self.and_inst[num].get_pin(n)
supply_offset = and_pin.ll().scale(0, 1)
self.add_rect(layer="m1",
self.add_rect(layer=and_pin.layer,
offset=supply_offset,
width=self.and_inst[num].rx())
# Add pins in two locations
for xoffset in [in_xoffset]:
pin_pos = vector(xoffset, and_pin.cy())
self.add_power_pin(n, pin_pos)
self.add_power_pin(name=n,
loc=pin_pos,
start_layer=and_pin.layer)

View File

@ -3,8 +3,6 @@
# Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved.
#
import sys
from tech import drc, parameter
from math import log
import debug
import design
@ -13,6 +11,7 @@ from vector import vector
from globals import OPTS
class port_address(design.design):
"""
Create the address port (row decoder and wordline driver)..
@ -25,17 +24,16 @@ class port_address(design.design):
self.addr_size = int(log(self.num_rows, 2))
if name == "":
name = "port_address_{0}_{1}".format(cols,rows)
name = "port_address_{0}_{1}".format(cols, rows)
design.design.__init__(self, name)
debug.info(2, "create data port of cols {0} rows {1}".format(cols,rows))
debug.info(2, "create data port of cols {0} rows {1}".format(cols, rows))
self.create_netlist()
if not OPTS.netlist_only:
debug.check(len(self.all_ports)<=2,"Bank layout cannot handle more than two ports.")
debug.check(len(self.all_ports) <= 2, "Bank layout cannot handle more than two ports.")
self.create_layout()
self.add_boundary()
def create_netlist(self):
self.add_pins()
self.add_modules()
@ -51,16 +49,15 @@ class port_address(design.design):
""" Adding pins for port address module"""
for bit in range(self.addr_size):
self.add_pin("addr_{0}".format(bit),"INPUT")
self.add_pin("addr_{0}".format(bit), "INPUT")
self.add_pin("wl_en", "INPUT")
for bit in range(self.num_rows):
self.add_pin("wl_{0}".format(bit),"OUTPUT")
self.add_pin("wl_{0}".format(bit), "OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def route_layout(self):
""" Create routing amoung the modules """
@ -71,8 +68,8 @@ class port_address(design.design):
def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts:
self.copy_power_pins(inst,"vdd")
self.copy_power_pins(inst,"gnd")
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
def route_pins(self):
for row in range(self.addr_size):
@ -119,12 +116,10 @@ class port_address(design.design):
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def create_wordline_driver(self):
""" Create the Wordline Driver """
self.wordline_driver_inst = self.add_inst(name="wordline_driver",
self.wordline_driver_inst = self.add_inst(name="wordline_driver",
mod=self.wordline_driver)
temp = []
@ -137,19 +132,13 @@ class port_address(design.design):
temp.append("gnd")
self.connect_inst(temp)
def place_instances(self):
"""
Compute the offsets and place the instances.
"""
# A space for wells or jogging m2
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"),
3*self.m2_pitch)
row_decoder_offset = vector(0,0)
wordline_driver_offset = vector(self.row_decoder.width + self.m2_gap,0)
row_decoder_offset = vector(0, 0)
wordline_driver_offset = vector(self.row_decoder.width, 0)
self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_offset)

View File

@ -694,7 +694,7 @@ class port_data(design.design):
# Channel route each mux separately since we don't minimize the number
# of tracks in teh channel router yet. If we did, we could route all the bits at once!
offset = bot_inst_group.inst.ul() + vector(0, self.m1_pitch)
offset = bot_inst_group.inst.ul() + vector(0, self.m1_nonpref_pitch)
for bit in range(num_bits):
bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_inst_group, bit)

View File

@ -354,32 +354,30 @@ class replica_bitcell_array(design.design):
width=self.width,
height=pin.height())
# Replica bitlines
for port in range(self.left_rbl+self.right_rbl):
for port in range(self.left_rbl + self.right_rbl):
inst = self.replica_col_inst[port]
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(),self.replica_bl_names[port]):
for (pin_name, bl_name) in zip(self.cell.get_all_bitline_names(), self.replica_bl_names[port]):
pin = inst.get_pin(pin_name)
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
name = bl_name
else:
name = "rbl_{0}_{1}".format(pin_name,port)
name = "rbl_{0}_{1}".format(pin_name, port)
self.add_layout_pin(text=name,
layer=pin.layer,
offset=pin.ll().scale(1,0),
offset=pin.ll().scale(1, 0),
width=pin.width(),
height=self.height)
for pin_name in ["vdd","gnd"]:
for pin_name in ["vdd", "gnd"]:
for inst in self.insts:
pin_list = inst.get_pins(pin_name)
for pin in pin_list:
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=True, start_layer=pin.layer)
self.add_power_pin(name=pin_name,
loc=pin.center(),
directions=("V", "V"),
start_layer=pin.layer)
def get_rbl_wl_name(self, port):
""" Return the WL for the given RBL port """
return self.rbl_wl_names[port]
@ -398,7 +396,7 @@ class replica_bitcell_array(design.design):
# Dynamic Power from Bitline
bl_wire = self.gen_bl_wire()
cell_load = 2 * bl_wire.return_input_cap()
cell_load = 2 * bl_wire.return_input_cap()
bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing)

View File

@ -130,13 +130,13 @@ class sense_amp_array(design.design):
self.add_power_pin(name="gnd",
loc=gnd_pin.center(),
start_layer=gnd_pin.layer,
vertical=True)
directions=("V", "V"))
vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd",
loc=vdd_pin.center(),
start_layer=vdd_pin.layer,
vertical=True)
directions=("V", "V"))
bl_pin = inst.get_pin(inst.mod.get_bl_names())
br_pin = inst.get_pin(inst.mod.get_br_names())

View File

@ -8,7 +8,7 @@
import debug
import design
import math
import contact
from tech import drc
from vector import vector
from sram_factory import factory
from globals import OPTS
@ -120,9 +120,11 @@ class wordline_driver(design.design):
self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
def place_drivers(self):
and2_xoffset = 2 * self.m1_width + 5 * self.m1_space
# Leave a well gap to separate the bitcell array well from this well
well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
self.width = and2_xoffset + self.decoders_per_row * self.and2.width
self.width = self.decoders_per_row * self.and2.width + well_gap
self.height = self.and2.height * self.driver_rows
for inst_index in range(self.bitcell_rows):
@ -136,7 +138,7 @@ class wordline_driver(design.design):
y_offset = self.and2.height * row
inst_mirror = "R0"
x_offset = and2_xoffset + dec * self.and2.width
x_offset = dec * self.and2.width
and2_offset = [x_offset, y_offset]
# add and2
@ -147,32 +149,26 @@ class wordline_driver(design.design):
""" Route all of the signals """
# Wordline enable connection
en_offset = [self.m1_width + 2 * self.m1_space, 0]
en_pin = self.and_inst[0].get_pin("B")
en_bottom_pos = vector(en_pin.lx(), 0)
en_pin = self.add_layout_pin(text="en",
layer="m2",
offset=en_offset,
width=self.m2_width,
offset=en_bottom_pos,
height=self.height)
for inst_index in range(self.bitcell_rows):
and_inst = self.and_inst[inst_index]
row = math.floor(inst_index / self.decoders_per_row)
dec = inst_index % self.decoders_per_row
# en connection
b_pin = and_inst.get_pin("B")
b_pos = b_pin.center()
clk_offset = vector(en_pin.bc().x, b_pos.y)
self.add_segment_center(layer="m2",
start=clk_offset,
end=b_pos)
self.add_via_center(layers=self.m1_stack,
offset=b_pos)
# Drop a via
b_pin = and_inst.get_pin("B")
self.add_via_stack_center(from_layer=b_pin.layer,
to_layer="m2",
offset=b_pin.center())
# connect the decoder input pin to and2 A
a_pin = and_inst.get_pin("A")
a_pos = a_pin.center()
a_offset = vector(clk_offset.x, a_pos.y)
# must under the clk line in M1
self.add_layout_pin_segment_center(text="in_{0}".format(row),
layer="m1",

View File

@ -165,7 +165,7 @@ class write_driver_array(design.design):
for pin in pin_list:
self.add_power_pin(name=n,
loc=pin.center(),
vertical=True,
directions=("V", "V"),
start_layer=pin.layer)
if self.write_size:
for bit in range(self.num_wmasks):

View File

@ -5,9 +5,7 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
from math import log
import design
from tech import drc
import debug
from sram_factory import factory
from vector import vector
@ -43,33 +41,28 @@ class write_mask_and_array(design.design):
self.add_pins()
self.create_and2_array()
def create_layout(self):
self.place_and2_array()
spacing = self.wmask_en_len - self.and2.width
self.width = (self.num_wmasks*self.and2.width) + ((self.num_wmasks-1)*spacing)
self.height = self.and2.height
self.add_layout_pins()
self.add_boundary()
self.DRC_LVS()
def add_pins(self):
for bit in range(self.num_wmasks):
self.add_pin("wmask_in_{}".format(bit),"INPUT")
self.add_pin("wmask_in_{}".format(bit), "INPUT")
self.add_pin("en", "INPUT")
for bit in range(self.num_wmasks):
self.add_pin("wmask_out_{}".format(bit),"OUTPUT")
self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND")
self.add_pin("wmask_out_{}".format(bit), "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
# Size the AND gate for the number of write drivers it drives, which is equal to the write size.
# Assume stage effort of 3 to compute the size
self.and2 = factory.create(module_type="pand2",
size=self.write_size/4.0)
size=self.write_size / 4.0)
self.add_mod(self.and2)
def create_and2_array(self):
self.and2_insts = {}
for bit in range(self.num_wmasks):
@ -81,7 +74,6 @@ class write_mask_and_array(design.design):
"wmask_out_{}".format(bit),
"vdd", "gnd"])
def place_and2_array(self):
# Place the write mask AND array at the start of each write driver enable length.
# This ensures the write mask AND array will be directly under the corresponding write driver enable wire.
@ -96,56 +88,45 @@ class write_mask_and_array(design.design):
self.wmask_en_len = self.words_per_row * (self.write_size * self.driver_spacing)
debug.check(self.wmask_en_len >= self.and2.width,
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,self.wmask_en_len))
"Write mask AND is wider than the corresponding write drivers {0} vs {1}.".format(self.and2.width,
self.wmask_en_len))
self.width = self.bitcell.width * self.columns
self.height = self.and2.height
for i in range(self.num_wmasks):
base = vector(i * self.wmask_en_len, 0)
self.and2_insts[i].place(base)
def add_layout_pins(self):
# Create the enable pin that connects all write mask AND array's B pins
beg_en_pin = self.and2_insts[0].get_pin("B")
end_en_pin = self.and2_insts[self.num_wmasks-1].get_pin("B")
if self.port % 2:
# Extend metal3 to edge of AND array in multiport
en_to_edge = self.and2.width - beg_en_pin.cx()
self.add_layout_pin(text="en",
layer="m3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx() + en_to_edge)
self.add_via_center(layers=self.m1_stack,
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
self.add_via_center(layers=self.m2_stack,
offset=vector(end_en_pin.cx() + en_to_edge, end_en_pin.cy()))
else:
self.add_layout_pin(text="en",
layer="m3",
offset=beg_en_pin.bc(),
width=end_en_pin.cx() - beg_en_pin.cx())
en_pin = self.and2_insts[0].get_pin("B")
self.add_layout_pin_segment_center(text="en",
layer="m3",
start=vector(0, en_pin.cy()),
end=vector(self.width, en_pin.cy()))
for i in range(self.num_wmasks):
# Copy remaining layout pins
self.copy_layout_pin(self.and2_insts[i],"A","wmask_in_{0}".format(i))
self.copy_layout_pin(self.and2_insts[i],"Z","wmask_out_{0}".format(i))
self.copy_layout_pin(self.and2_insts[i], "A", "wmask_in_{0}".format(i))
self.copy_layout_pin(self.and2_insts[i], "Z", "wmask_out_{0}".format(i))
# Add via connections to metal3 for AND array's B pin
en_pin = self.and2_insts[i].get_pin("B")
self.add_via_center(layers=self.m1_stack,
offset=en_pin.center())
self.add_via_center(layers=self.m2_stack,
offset=en_pin.center())
en_pos = en_pin.center()
self.add_via_stack_center(from_layer=en_pin.layer,
to_layer="m3",
offset=en_pos)
for supply in ["gnd", "vdd"]:
supply_pin=self.and2_insts[i].get_pin(supply)
self.add_power_pin(supply, supply_pin.center())
for supply in ["gnd", "vdd"]:
supply_pin_left = self.and2_insts[0].get_pin(supply)
supply_pin_right = self.and2_insts[self.num_wmasks-1].get_pin(supply)
self.add_path("m1",[supply_pin_left.lc(), supply_pin_right.rc()])
supply_pin_right = self.and2_insts[self.num_wmasks - 1].get_pin(supply)
self.add_path("m1", [supply_pin_left.lc(), supply_pin_right.rc()])
def get_cin(self):
"""Get the relative capacitance of all the input connections in the bank"""

View File

@ -44,6 +44,7 @@ class pand2(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
@ -80,22 +81,6 @@ class pand2(pgate.pgate):
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,

View File

@ -44,6 +44,7 @@ class pand3(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
self.DRC_LVS()
@ -81,22 +82,6 @@ class pand3(pgate.pgate):
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=pin.layer,

View File

@ -37,6 +37,7 @@ class pbuf(pgate.pgate):
self.place_insts()
self.add_wires()
self.add_layout_pins()
self.route_supply_rails()
self.add_boundary()
def add_pins(self):
@ -78,26 +79,9 @@ class pbuf(pgate.pgate):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
self.add_zjog(self.route_layer, z1_pin.center(), a2_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer,

View File

@ -76,6 +76,7 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height
self.route_supply_rails()
self.add_boundary()
def add_pins(self):
@ -146,21 +147,6 @@ class pdriver(pgate.pgate):
a_inst_list[x].center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin = self.inv_inst_list[0].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="m1",
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin = self.inv_inst_list[0].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="m1",
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
self.add_layout_pin_rect_center(text="Z",

View File

@ -17,6 +17,7 @@ from globals import OPTS
if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement
class pgate(design.design):
"""
This is a module that implements some shared
@ -30,8 +31,8 @@ class pgate(design.design):
if height:
self.height = height
elif not height:
# By default, we make it 10 M1 pitch tall
self.height = 10*self.m1_pitch
# By default, something simple
self.height = 14 * self.m1_pitch
if "li" in layer:
self.route_layer = "li"
@ -40,7 +41,15 @@ class pgate(design.design):
self.route_layer_width = getattr(self, "{}_width".format(self.route_layer))
self.route_layer_space = getattr(self, "{}_space".format(self.route_layer))
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
# This is the space from a S/D contact to the supply rail
# Assume the contact starts at the active edge
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
self.top_bottom_space = max(contact_to_vdd_rail_space,
poly_to_poly_gate_space)
self.create_netlist()
if not OPTS.netlist_only:
self.create_layout()
@ -68,13 +77,14 @@ class pgate(design.design):
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.", -1)
debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.")
self.add_rect(layer=source_pin.layer,
offset=source_pin.ll(),
height=height,
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left"):
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", directions=None):
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
@ -100,8 +110,6 @@ class pgate(design.design):
# Center is completely symmetric.
contact_width = contact.poly_contact.width
contact_m1_width = contact.poly_contact.second_layer_width
contact_m1_height = contact.poly_contact.second_layer_height
if position == "center":
contact_offset = left_gate_offset \
@ -118,18 +126,16 @@ class pgate(design.design):
else:
debug.error("Invalid contact placement option.", -1)
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
via = self.add_via_stack_center(from_layer="poly",
to_layer=self.route_layer,
offset=contact_offset,
directions=directions)
self.add_layout_pin_rect_center(text=name,
layer="m1",
layer=self.route_layer,
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
width=via.mod.second_layer_width,
height=via.mod.second_layer_height)
# This is to ensure that the contact is
# connected to the gate
mid_point = contact_offset.scale(0.5, 1) \
@ -201,12 +207,10 @@ class pgate(design.design):
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset,
implant_type="n",
well_type="n")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
well_type="n",
directions=("V", "V"))
self.add_rect_center(layer="m1",
self.add_rect_center(layer=self.route_layer,
offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y)
@ -256,13 +260,10 @@ class pgate(design.design):
self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset,
implant_type="p",
well_type="p")
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
self.add_rect_center(layer="m1",
well_type="p",
directions=("V", "V"))
self.add_rect_center(layer=self.route_layer,
offset=contact_offset.scale(1, 0.5),
width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y)
@ -286,6 +287,18 @@ class pgate(design.design):
# offset=implant_offset,
# width=implant_width,
# height=implant_height)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer=self.route_layer,
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer=self.route_layer,
offset=vector(0.5 * self.width, self.height),
width=self.width)
def determine_width(self):
""" Determine the width based on the well contacts (assumed to be on the right side) """

View File

@ -107,13 +107,6 @@ class pinv(pgate.pgate):
min_channel = max(contact.poly_contact.width + self.m1_space,
contact.poly_contact.width + 2 * self.poly_to_active)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
# debug.check(self.height > total_height,
# "Cell height {0} too small for simple min height {1}.".format(self.height,
@ -201,7 +194,7 @@ class pinv(pgate.pgate):
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True,
connect_drain_active=True)
@ -211,24 +204,12 @@ class pinv(pgate.pgate):
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True,
connect_drain_active=True)
self.add_mod(self.pmos)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Create the PMOS and NMOS netlist.

View File

@ -76,7 +76,7 @@ class pnand2(pgate.pgate):
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.nmos_left)
@ -92,7 +92,7 @@ class pnand2(pgate.pgate):
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left)
@ -101,43 +101,17 @@ class pnand2(pgate.pgate):
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact="m1")
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location,
# but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos_left.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5*self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Add PMOS and NMOS to the netlist.
@ -200,35 +174,46 @@ class pnand2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
# Top of NMOS drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.uy()
self.inputA_yoffset = bottom_pin_offset + self.m1_pitch
self.inputB_yoffset = self.inputA_yoffset + self.m3_pitch
# This will help with the wells and the input/output placement
inputB_yoffset = self.pmos2_inst.by() - max(self.poly_extend_active + contact.poly_contact.height,
self.m1_space + 0.5 * contact.m1_via.height)
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
self.inputB_yoffset,
"B",
position="right")
position="center")
self.inputA_yoffset = self.nmos2_inst.uy() + max(self.poly_extend_active + contact.poly_contact.height,
self.m1_space + 0.5 * contact.m1_via.height)
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
"A",
position="center")
def route_output(self):
""" Route the Z output """
# One routing track layer below the PMOS contacts
route_layer_offset = 0.5 * self.route_layer_width + self.route_layer_space
output_yoffset = self.pmos1_inst.get_pin("D").by() - route_layer_offset
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
top_pin_offset = pmos_pin.bc()
# NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.center()
bottom_pin_offset = nmos_pin.uc()
# Output pin
out_offset = vector(nmos_pin.cx(),
self.inputA_yoffset)
out_offset = vector(nmos_pin.cx() + self.route_layer_pitch,
output_yoffset)
# This routes on M2
# # Midpoints of the L routes go horizontal first then vertical
@ -253,10 +238,15 @@ class pnand2(pgate.pgate):
# This routes on route_layer
# Midpoints of the L routes goes vertical first then horizontal
mid1_offset = vector(top_pin_offset.x, out_offset.y)
top_mid_offset = vector(top_pin_offset.x, out_offset.y)
# Top transistors
self.add_path(self.route_layer,
[top_pin_offset, mid1_offset, out_offset, bottom_pin_offset])
[top_pin_offset, top_mid_offset, out_offset])
bottom_mid_offset = bottom_pin_offset + vector(0, self.route_layer_pitch)
# Bottom transistors
self.add_path(self.route_layer,
[out_offset, bottom_mid_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import contact
import pgate
import debug
from tech import drc, parameter, spice
@ -14,6 +13,7 @@ import logical_effort
from sram_factory import factory
from globals import OPTS
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -94,7 +94,7 @@ class pnand3(pgate.pgate):
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.nmos_left)
@ -102,7 +102,7 @@ class pnand3(pgate.pgate):
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left)
@ -111,14 +111,14 @@ class pnand3(pgate.pgate):
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact="m1")
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_center)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right)
@ -132,21 +132,6 @@ class pnand3(pgate.pgate):
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
@ -223,37 +208,34 @@ class pnand3(pgate.pgate):
def route_inputs(self):
""" Route the A and B and C inputs """
m1_pitch = self.m1_space + contact.m1_via.first_layer_height
# Put B right on the well line
self.inputB_yoffset = self.nwell_y_offset
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
# FIXME: constant hack
self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
# FIXME: constant hack
if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="left")
# Put B right on the well line
self.inputB_yoffset = self.inputA_yoffset - self.m1_pitch
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
self.inputB_yoffset,
"B",
position="center")
self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
self.inputC_yoffset,
"C",
position="right")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
@ -262,7 +244,7 @@ class pnand3(pgate.pgate):
nmos3_pin = self.nmos3_inst.get_pin("D")
out_offset = vector(nmos3_pin.cx() + self.route_layer_pitch,
self.inputA_yoffset)
self.output_yoffset)
# Go up to metal2 for ease on all output pins
# self.add_via_center(layers=self.m1_stack,

View File

@ -75,7 +75,7 @@ class pnor2(pgate.pgate):
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_left)
@ -84,14 +84,14 @@ class pnor2(pgate.pgate):
mults=self.tx_mults,
tx_type="nmos",
add_source_contact=self.route_layer,
add_drain_contact="m1")
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="m1",
add_source_contact=self.route_layer,
add_drain_contact="active")
self.add_mod(self.pmos_left)
@ -106,12 +106,6 @@ class pnor2(pgate.pgate):
def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """
# metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width)
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
self.overlap_offset = self.pmos_right.get_pin("D").center() - self.pmos_left.get_pin("S").center()
@ -124,27 +118,7 @@ class pnor2(pgate.pgate):
+ 0.5 * self.nwell_enclose_active
self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos_right.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active,
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="m1",
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="m1",
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
@ -172,10 +146,19 @@ class pnor2(pgate.pgate):
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
# Some of the S/D contacts may extend beyond the active,
# but this needs to be done in the gate itself
contact_extend_active_space = max(-self.nmos_right.get_pin("D").by(), 0)
# Assume the contact starts at the active edge
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space + contact_extend_active_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
# Recompute this since it has a small txwith the added contact extend active spacing
self.top_bottom_space = max(contact_to_vdd_rail_space,
poly_to_poly_gate_space)
pmos1_pos = vector(self.pmos_right.active_offset.x,
self.height - self.pmos_right.active_height \
- self.top_bottom_space)
self.height - self.pmos_right.active_height - self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
@ -186,7 +169,6 @@ class pnor2(pgate.pgate):
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -205,22 +187,28 @@ class pnor2(pgate.pgate):
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_inst.uy() + contact.poly_contact.height
# Top of NMOS drain
nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.uy()
self.inputB_yoffset = bottom_pin_offset + self.m1_nonpref_pitch
self.inputA_yoffset = self.inputB_yoffset + self.m1_nonpref_pitch
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
self.inputB_yoffset,
"B",
position="right")
position="right",
directions=("V", "V"))
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
"A",
directions=("V", "V"))
self.output_yoffset = self.inputA_yoffset + self.input_spacing
self.output_yoffset = self.inputA_yoffset + self.m1_nonpref_pitch
def route_output(self):
""" Route the Z output """

View File

@ -159,7 +159,7 @@ class precharge(design.design):
self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout with 2 M2 tracks
ydiff = self.pmos.height + self.m2_pitch
ydiff = self.pmos.height + 2 * self.m2_pitch
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos)

View File

@ -79,9 +79,6 @@ class ptristate_inv(pgate.pgate):
self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below
self.top_bottom_space = max(contact.active_contact.width, contact.active_contact.height)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",

View File

@ -38,15 +38,18 @@ class ptx(design.design):
connect_poly=False,
num_contacts=None):
if not add_source_contact and "li" in layer:
add_source_contact = "li"
elif not add_source_contact:
add_source_contact = "m1"
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
if not add_drain_contact and "li" in layer:
add_drain_contact = "li"
elif not add_drain_contact:
add_drain_contact = "m1"
# Default contacts are the lowest layer
if not add_source_contact:
add_source_contact = self.route_layer
# Default contacts are the lowest layer
if not add_drain_contact:
add_drain_contact = self.route_layer
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
@ -81,10 +84,6 @@ class ptx(design.design):
self.series_devices = series_devices
self.num_contacts = num_contacts
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.route_layer_width = drc("minwidth_{}".format(self.route_layer))
self.route_layer_space = drc("{0}_to_{0}".format(self.route_layer))
@ -286,23 +285,16 @@ class ptx(design.design):
width=poly_width,
height=self.poly_width)
def connect_fingered_active(self, positions, pin_name, layer, top):
def connect_fingered_active(self, positions, pin_name, top):
"""
Connect each contact up/down to a source or drain pin
"""
if top:
dir = 1
else:
dir = -1
if len(positions) <= 1:
return
debug.check(layer != "active", "Must specify a metal for source connections.")
layer_space = getattr(self, "{}_space".format(layer))
layer_width = getattr(self, "{}_width".format(layer))
layer_space = getattr(self, "{}_space".format(self.route_layer))
layer_width = getattr(self, "{}_width".format(self.route_layer))
# This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts
@ -311,16 +303,21 @@ class ptx(design.design):
# This is the width of a m1 extend the ends of the pin
end_offset = vector(layer_width / 2.0, 0)
offset = pin_offset.scale(dir, dir)
# We move the opposite direction from the bottom
if not top:
offset = pin_offset.scale(-1, -1)
else:
offset = pin_offset
# remove the individual connections
self.remove_layout_pin(pin_name)
# Add each vertical segment
for a in positions:
self.add_path(self.add_source_contact,
[a, a + pin_offset.scale(dir, dir)])
self.add_path(self.route_layer,
[a, a + offset])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text=pin_name,
layer=layer,
layer=self.route_layer,
start=positions[0] + offset - end_offset,
end=positions[-1] + offset + end_offset)
@ -475,10 +472,10 @@ class ptx(design.design):
offset=pos)
if self.connect_source_active:
self.connect_fingered_active(source_positions, "S", self.add_source_contact, top=(self.tx_type=="pmos"))
self.connect_fingered_active(source_positions, "S", top=(self.tx_type=="pmos"))
if self.connect_drain_active:
self.connect_fingered_active(drain_positions, "D", self.add_drain_contact, top=(self.tx_type=="nmos"))
self.connect_fingered_active(drain_positions, "D", top=(self.tx_type=="nmos"))
def get_stage_effort(self, cout):
"""Returns an object representing the parameters for delay in tau units."""
@ -509,21 +506,24 @@ class ptx(design.design):
else:
debug.error("Invalid source drain name.")
via=self.add_via_stack_center(offset=pos,
from_layer="active",
to_layer=layer,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
if layer != "active":
via=self.add_via_stack_center(offset=pos,
from_layer="active",
to_layer=layer,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
if layer == "active":
source_via = getattr(contact, "{}_contact".format(layer))
pin_height = via.mod.second_layer_height
pin_width = via.mod.second_layer_width
else:
source_via = getattr(contact, "{}_via".format(layer))
via = None
pin_height = None
pin_width = None
# Source drain vias are all vertical
pin_height = source_via.first_layer_width
pin_width = source_via.first_layer_height
self.add_layout_pin_rect_center(text=label,
layer=layer,
offset=pos,

View File

@ -18,17 +18,13 @@ class router_tech:
"""
def __init__(self, layers, rail_track_width):
"""
Allows us to change the layers that we are routing on. First layer
is always horizontal, middle is via, and last is always
vertical.
Allows us to change the layers that we are routing on.
This uses the preferreed directions.
"""
self.layers = layers
self.rail_track_width = rail_track_width
if len(self.layers) == 1:
if preferred_directions[self.layers[0]] != "H":
debug.warning("Using '{}' for horizontal routing, but it " \
"prefers vertical routing".format(self.layers[0]))
self.horiz_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_lpp = self.vert_lpp = layer[self.layers[0]]
@ -42,13 +38,21 @@ class router_tech:
# figure out wich of the two layers prefers horizontal/vertical
# routing
if preferred_directions[try_horiz_layer] == "H" and preferred_directions[try_vert_layer] == "V":
self.horiz_layer_name = None
self.vert_layer_name = None
if preferred_directions[try_horiz_layer] == "H":
self.horiz_layer_name = try_horiz_layer
else:
self.horiz_layer_name = try_vert_layer
if preferred_directions[try_vert_layer] == "V":
self.vert_layer_name = try_vert_layer
else:
raise ValueError("Layer '{}' and '{}' are using the wrong " \
"preferred_directions '{}' and '{}'. Only "\
"('H', 'V') are supported")
self.vert_layer_name = try_horiz_layer
if not self.horiz_layer_name or not self.vert_layer_name:
raise ValueError("Layer '{}' and '{}' are using the wrong "
"preferred_directions '{}' and '{}'.")
via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height)

View File

@ -59,14 +59,23 @@ class sram_1bank(sram_base):
wmask_pos = [None] * len(self.all_ports)
data_pos = [None] * len(self.all_ports)
# These positions utilize the channel route sizes.
# FIXME: Auto-compute these rather than manual computation.
# If a horizontal channel, they rely on the vertical channel non-preferred (contacted) pitch.
# If a vertical channel, they rely on the horizontal channel non-preferred (contacted) pitch.
# So, m3 non-pref pitch means that this is routed on the m2 layer.
if self.write_size:
max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch
max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch
self.data_bus_gap = self.m4_nonpref_pitch * 2
self.data_bus_size = self.m4_nonpref_pitch * (self.word_size) + self.data_bus_gap
self.wmask_bus_gap = self.m2_nonpref_pitch * 2
self.wmask_bus_size = self.m2_nonpref_pitch * (max(self.num_wmasks + 1, self.col_addr_size + 1)) + self.wmask_bus_gap
else:
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk
# The M1 pitch is for supply rail spacings
max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch
self.data_bus_gap = self.m3_nonpref_pitch * 2
self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap
self.col_addr_bus_gap = self.m2_nonpref_pitch * 2
self.col_addr_bus_size = self.m2_nonpref_pitch * (self.col_addr_size) + self.col_addr_bus_gap
# Port 0
port = 0
@ -74,12 +83,12 @@ class sram_1bank(sram_base):
if self.write_size:
# Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size_wmask - self.dff.height)
- self.wmask_bus_size - self.dff.height)
self.wmask_dff_insts[port].place(wmask_pos[port])
# Add the data flops below the write mask flops.
data_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size - max_gap_size_wmask - 2 * self.dff.height)
- self.data_bus_size - self.wmask_bus_size - 2 * self.dff.height)
self.data_dff_insts[port].place(data_pos[port])
else:
# Add the data flops below the bank to the right of the lower-left of bank array
@ -89,7 +98,7 @@ class sram_1bank(sram_base):
# sense amps.
if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x,
-max_gap_size - self.dff.height)
-self.data_bus_size - self.dff.height)
self.data_dff_insts[port].place(data_pos[port])
else:
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0)
@ -99,10 +108,10 @@ class sram_1bank(sram_base):
if self.col_addr_dff:
if self.write_size:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size_wmask - self.col_addr_dff_insts[port].height)
-self.wmask_bus_size - self.col_addr_dff_insts[port].height)
else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap,
-max_gap_size - self.col_addr_dff_insts[port].height)
-self.data_bus_size - self.col_addr_dff_insts[port].height)
self.col_addr_dff_insts[port].place(col_addr_pos[port])
else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0)
@ -127,12 +136,12 @@ class sram_1bank(sram_base):
if self.write_size:
# Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(self.bank.bank_array_ur.x - self.wmask_dff_insts[port].width,
self.bank.height + max_gap_size_wmask + self.dff.height)
self.bank.height + self.wmask_bus_size + self.dff.height)
self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
# Add the data flops below the write mask flops
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size_wmask + max_gap_size + 2 * self.dff.height)
self.bank.height + self.wmask_bus_size + self.data_bus_size + 2 * self.dff.height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
else:
# Add the data flops above the bank to the left of the upper-right of bank array
@ -141,17 +150,17 @@ class sram_1bank(sram_base):
# These flops go below the sensing and leave a gap to channel route to the
# sense amps.
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width,
self.bank.height + max_gap_size + self.dff.height)
self.bank.height + self.data_bus_size + self.dff.height)
self.data_dff_insts[port].place(data_pos[port], mirror="MX")
# Add the col address flops above the bank to the right of the upper-right of bank array
if self.col_addr_dff:
if self.write_size:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank.height + max_gap_size_wmask + self.dff.height)
self.bank.height + self.wmask_bus_size + self.dff.height)
else:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap,
self.bank.height + max_gap_size + self.dff.height)
self.bank.height + self.data_bus_size + self.dff.height)
self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
else:
col_addr_pos[port] = self.bank_inst.ur()
@ -245,7 +254,7 @@ class sram_1bank(sram_base):
# This uses a metal2 track to the right (for port0) of the control/row addr DFF
# to route vertically. For port1, it is to the left.
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk")
if port%2:
if port % 2:
control_clk_buf_pos = control_clk_buf_pin.lc()
row_addr_clk_pos = row_addr_clk_pin.lc()
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch,
@ -258,19 +267,20 @@ class sram_1bank(sram_base):
# This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y)
self.add_path("m1", [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=self.m1_stack,
offset=clk_steiner_pos)
self.add_path(control_clk_buf_pin.layer, [control_clk_buf_pos, clk_steiner_pos])
self.add_via_stack_center(from_layer=control_clk_buf_pin.layer,
to_layer="m2",
offset=clk_steiner_pos)
# Note, the via to the control logic is taken care of above
self.add_wire(("m3", "via2", "m2"),
self.add_wire(self.m2_stack[::-1],
[row_addr_clk_pos, mid1_pos, clk_steiner_pos])
if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y)
self.add_wire(("m3", "via2", "m2"),
self.add_wire(self.m2_stack[::-1],
[dff_clk_pos, mid_pos, clk_steiner_pos])
if port in self.write_ports:
@ -282,7 +292,7 @@ class sram_1bank(sram_base):
self.add_path("m2",
[mid_pos, clk_steiner_pos],
width=max(m2_via.width, m2_via.height))
self.add_wire(("m3", "via2", "m2"),
self.add_wire(self.m2_stack[::-1],
[data_dff_clk_pos, mid_pos, clk_steiner_pos])
if self.write_size:
@ -292,7 +302,7 @@ class sram_1bank(sram_base):
# In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts
self.add_path("m2", [mid_pos, clk_steiner_pos], width=max(m2_via.width, m2_via.height))
self.add_wire(("m3", "via2", "m2"), [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
self.add_wire(self.m2_stack[::-1], [wmask_dff_clk_pos, mid_pos, clk_steiner_pos])
def route_control_logic(self):
""" Route the control logic pins that are not inputs """
@ -304,14 +314,13 @@ class sram_1bank(sram_base):
continue
src_pin = self.control_logic_insts[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port))
self.connect_vbus_m2m3(src_pin, dest_pin)
self.connect_vbus(src_pin, dest_pin)
for port in self.all_ports:
# Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
self.connect_hbus_m2m3(src_pin, dest_pin)
self.connect_hbus(src_pin, dest_pin)
def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """
@ -324,33 +333,37 @@ class sram_1bank(sram_base):
flop_pos = flop_pin.center()
bank_pos = bank_pin.center()
mid_pos = vector(bank_pos.x, flop_pos.y)
self.add_wire(("m3", "via2", "m2"),
self.add_wire(self.m2_stack[::-1],
[flop_pos, mid_pos, bank_pos])
self.add_via_center(layers=self.m2_stack,
offset=flop_pos)
self.add_via_stack_center(from_layer=flop_pin.layer,
to_layer="m3",
offset=flop_pos)
def route_col_addr_dff(self):
""" Connect the output of the col flops to the bank pins """
for port in self.all_ports:
if port%2:
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch)
if port % 2:
offset = self.col_addr_dff_insts[port].ll() - vector(0, self.col_addr_bus_size)
else:
offset = self.col_addr_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
offset = self.col_addr_dff_insts[port].ul() + vector(0, self.col_addr_bus_gap)
bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=offset,
names=bus_names,
length=self.col_addr_dff_insts[port].width)
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
data_dff_map = zip(dff_names, bus_names)
self.connect_horizontal_bus(data_dff_map, self.col_addr_dff_insts[port], col_addr_bus_offsets)
self.connect_horizontal_bus(data_dff_map,
self.col_addr_dff_insts[port],
col_addr_bus_offsets)
bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
data_bank_map = zip(bank_names, bus_names)
self.connect_horizontal_bus(data_bank_map, self.bank_inst, col_addr_bus_offsets)
self.connect_horizontal_bus(data_bank_map,
self.bank_inst,
col_addr_bus_offsets)
def route_data_dff(self):
""" Connect the output of the data flops to the write driver """
@ -358,48 +371,47 @@ class sram_1bank(sram_base):
for port in self.write_ports:
if self.write_size:
if port % 2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m3_pitch)
offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size)
else:
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m3_pitch)
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
else:
if port % 2:
offset = self.data_dff_insts[port].ll() - vector(0, (self.word_size + 2) * self.m1_pitch)
offset = self.data_dff_insts[port].ll() - vector(0, self.data_bus_size)
else:
offset = self.data_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
offset = self.data_dff_insts[port].ul() + vector(0, self.data_bus_gap)
dff_names = ["dout_{}".format(x) for x in range(self.word_size)]
dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
if self.write_size:
for x in dff_names:
pin_offset = self.data_dff_insts[port].get_pin(x).center()
pin = self.data_dff_insts[port].get_pin(x)
pin_offset = pin.center()
self.add_via_center(layers=self.m1_stack,
offset=pin_offset,
directions=("V", "V"))
self.add_via_center(layers=self.m2_stack,
offset=pin_offset)
self.add_via_center(layers=self.m3_stack,
offset=pin_offset)
self.add_via_stack_center(from_layer="m2",
to_layer="m4",
offset=pin_offset)
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)]
bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
if self.write_size:
for x in bank_names:
pin = self.bank_inst.get_pin(x)
if port % 2:
pin_offset = self.bank_inst.get_pin(x).uc()
pin_offset = pin.uc()
else:
pin_offset = self.bank_inst.get_pin(x).bc()
self.add_via_center(layers=self.m1_stack,
offset=pin_offset)
self.add_via_center(layers=self.m2_stack,
offset=pin_offset)
self.add_via_center(layers=self.m3_stack,
offset=pin_offset)
pin_offset = pin.bc()
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m4",
offset=pin_offset)
route_map = list(zip(bank_pins, dff_pins))
if self.write_size:
layer_stack = self.m3_stack
else:
layer_stack = self.m1_stack
self.create_horizontal_channel_route(netlist=route_map,
offset=offset,
layer_stack=layer_stack)
@ -409,9 +421,9 @@ class sram_1bank(sram_base):
# This is where the channel will start (y-dimension at least)
for port in self.write_ports:
if port % 2:
offset = self.wmask_dff_insts[port].ll() - vector(0, (self.num_wmasks + 2) * self.m1_pitch)
offset = self.wmask_dff_insts[port].ll() - vector(0, self.wmask_bus_size)
else:
offset = self.wmask_dff_insts[port].ul() + vector(0, 2 * self.m1_pitch)
offset = self.wmask_dff_insts[port].ul() + vector(0, self.wmask_bus_gap)
dff_names = ["dout_{}".format(x) for x in range(self.num_wmasks)]
dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]

View File

@ -7,6 +7,7 @@
#
import datetime
import debug
from math import log
from importlib import reload
from vector import vector
from globals import OPTS, print_time
@ -137,7 +138,6 @@ class sram_base(design, verilog, lef):
self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst, "gnd")
import tech
if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins)
return
@ -148,6 +148,7 @@ class sram_base(design, verilog, lef):
grid_stack = power_grid
except ImportError:
# if no power_grid is specified by tech we use sensible defaults
import tech
if "m4" in tech.layer:
# Route a M3/M4 grid
grid_stack = self.m3_stack
@ -496,70 +497,6 @@ class sram_base(design, verilog, lef):
self.connect_inst(temp)
return insts
def connect_vbus_m2m3(self, src_pin, dest_pin):
"""
Helper routine to connect an instance to a vertical bus.
Routes horizontal then vertical L shape.
Dest pin is assumed to be on M2.
Src pin can be on M1/M2/M3.
"""
if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc()
else:
in_pos = src_pin.lc()
if src_pin.cy() < dest_pin.cy():
out_pos = dest_pin.bc()
else:
out_pos = dest_pin.uc()
# move horizontal first
self.add_wire(("m3", "via2", "m2"),
[in_pos,
vector(out_pos.x, in_pos.y),
out_pos])
if src_pin.layer=="m1":
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
if src_pin.layer in ["m1", "m2"]:
self.add_via_center(layers=self.m2_stack,
offset=in_pos)
def connect_hbus_m2m3(self, src_pin, dest_pin):
"""
Helper routine to connect an instance to a horizontal bus.
Routes horizontal then vertical L shape.
Dest pin is on M1/M2/M3.
Src pin can be on M1/M2/M3.
"""
if src_pin.cx()<dest_pin.cx():
in_pos = src_pin.rc()
else:
in_pos = src_pin.lc()
if src_pin.cy() < dest_pin.cy():
out_pos = dest_pin.lc()
else:
out_pos = dest_pin.rc()
# move horizontal first
self.add_wire(("m3", "via2", "m2"),
[in_pos,
vector(out_pos.x, in_pos.y),
out_pos])
if src_pin.layer=="m1":
self.add_via_center(layers=self.m1_stack,
offset=in_pos)
if src_pin.layer in ["m1", "m2"]:
self.add_via_center(layers=self.m2_stack,
offset=in_pos)
if dest_pin.layer=="m1":
self.add_via_center(layers=self.m1_stack,
offset=out_pos)
if dest_pin.layer=="m3":
self.add_via_center(layers=self.m2_stack,
offset=out_pos)
def sp_write(self, sp_name, lvs_netlist=False):
# Write the entire spice of the object to the file

View File

@ -25,7 +25,7 @@ class pinv_test(openram_test):
tx = factory.create(module_type="pinv", size=1)
self.local_check(tx)
globals.end_openram()
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":

View File

@ -21,35 +21,32 @@ class hierarchical_decoder_test(openram_test):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
# check hierarchical decoder for single port
# Checks 2x4 and 2-input NAND decoder
debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a)
# Checks 2x4 and 2-input NAND decoder with non-power-of-two
debug.info(1, "Testing 17 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a)
debug.info(1, "Testing 23 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a)
# Checks 2x4 with 3x8 and 2-input NAND decoder
debug.info(1, "Testing 32 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a)
debug.info(1, "Testing 65 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
# Checks 3 x 2x4 and 3-input NAND decoder
debug.info(1, "Testing 64 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a)
debug.info(1, "Testing 341 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
# Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
debug.info(1, "Testing 132 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
self.local_check(a)
# Checks 3 x 3x8 and 3-input NAND decoder
debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a)

View File

@ -8,32 +8,25 @@
#
import unittest
from testutils import *
import sys,os
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class control_logic_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
import control_logic
import tech
debug.info(1, "Testing sample for control_logic_rw")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
self.local_check(a)
debug.info(1, "Testing sample for control_logic_r")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
self.local_check(a)
debug.info(1, "Testing sample for control_logic_w")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":

View File

@ -25,28 +25,28 @@ class single_bank_test(openram_test):
c = sram_config(word_size=4,
num_words=16)
c.words_per_row=1
factory.reset()
c.recompute_sizes()
debug.info(1, "No column mux")
a = factory.create("bank", sram_config=c)
self.local_check(a)
# c.words_per_row=1
# factory.reset()
# c.recompute_sizes()
# debug.info(1, "No column mux")
# a = factory.create("bank", sram_config=c)
# self.local_check(a)
c.num_words=32
c.words_per_row=2
factory.reset()
c.recompute_sizes()
debug.info(1, "Two way column mux")
a = factory.create("bank", sram_config=c)
self.local_check(a)
# c.num_words=32
# c.words_per_row=2
# factory.reset()
# c.recompute_sizes()
# debug.info(1, "Two way column mux")
# a = factory.create("bank", sram_config=c)
# self.local_check(a)
c.num_words=64
c.words_per_row=4
factory.reset()
c.recompute_sizes()
debug.info(1, "Four way column mux")
a = factory.create("bank", sram_config=c)
self.local_check(a)
# c.num_words=64
# c.words_per_row=4
# factory.reset()
# c.recompute_sizes()
# debug.info(1, "Four way column mux")
# a = factory.create("bank", sram_config=c)
# self.local_check(a)
c.word_size=2
c.num_words=128

View File

@ -70,20 +70,20 @@ class timing_sram_test(openram_test):
'min_period': 0.898,
'disabled_write1_power': [0.201411]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'read1_power': [12.11658],
'write1_power': [10.52653],
'read0_power': [11.956710000000001],
'disabled_write0_power': [7.673665],
'disabled_write1_power': [7.981922000000001],
'slew_lh': [1.868836],
'slew_hl': [1.868836],
'delay_hl': [1.8598510000000001],
'delay_lh': [1.8598510000000001],
'leakage_power': 0.008613619,
'disabled_read0_power': [5.904712],
'min_period': 6.875,
'disabled_read1_power': [7.132159],
'write0_power': [13.406400000000001]}
golden_data = {'delay_hl': [1.8435739999999998],
'delay_lh': [1.8435739999999998],
'disabled_read0_power': [5.917947],
'disabled_read1_power': [7.154297],
'disabled_write0_power': [7.696351],
'disabled_write1_power': [7.999409000000001],
'leakage_power': 0.004809726,
'min_period': 6.875,
'read0_power': [11.833079999999999],
'read1_power': [11.99236],
'slew_hl': [1.8668490000000002],
'slew_lh': [1.8668490000000002],
'write0_power': [13.287510000000001],
'write1_power': [10.416369999999999]}
else:
self.assertTrue(False) # other techs fail

View File

@ -32,7 +32,7 @@ tech_modules = module_type()
cell_properties = cell_properties()
cell_properties.bitcell.mirror.x = True
cell_properties.bitcell.mirror.y = False
cell_properties.bitcell_power_pin_directions = ("V", "V")
###################################################
# GDS file info
@ -63,6 +63,13 @@ m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4")
layer_indices = {"poly": 0,
"active": 0,
"m1": 1,
"m2": 2,
"m3": 3,
"m4": 4}
# The FEOL stacks get us up to m1
feol_stacks = [poly_stack,
active_stack]

View File

@ -60,6 +60,13 @@ m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4")
layer_indices = {"poly": 0,
"active": 0,
"m1": 1,
"m2": 2,
"m3": 3,
"m4": 4}
# The FEOL stacks get us up to m1
feol_stacks = [poly_stack,
active_stack]