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.layer_stack = layer_stack
self.dimensions = dimensions 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 self.directions = directions
# Preferred directions
else: else:
self.directions = (tech.preferred_directions[layer_stack[0]], self.directions = (tech.preferred_directions[layer_stack[0]],
tech.preferred_directions[layer_stack[2]]) tech.preferred_directions[layer_stack[2]])

View File

@ -6,6 +6,7 @@
# All rights reserved. # All rights reserved.
# #
from hierarchy_design import hierarchy_design from hierarchy_design import hierarchy_design
from utils import round_to_grid
import contact import contact
from globals import OPTS from globals import OPTS
import re import re
@ -31,48 +32,98 @@ class design(hierarchy_design):
in many places in the compiler. in many places in the compiler.
""" """
from tech import layer_indices
import tech import tech
for key in dir(tech): for layer in layer_indices:
# Single layer width rules key = "{}_stack".format(layer)
match = re.match(r".*_stack$", key)
if match: # Set the stack as a local helper
try:
layer_stack = getattr(tech, key) layer_stack = getattr(tech, key)
# Set the stack as a local helper
setattr(self, key, layer_stack) setattr(self, key, layer_stack)
except AttributeError:
pass
# Add the pitch # Skip computing the pitch for active
setattr(self, if layer == "active":
"{}_pitch".format(layer_stack[0]), continue
self.compute_pitch(layer_stack))
# 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: if False:
print("m1_pitch", self.m1_pitch) from tech import preferred_directions
print("m2_pitch", self.m2_pitch) print(preferred_directions)
print("m3_pitch", self.m3_pitch) 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 import sys
sys.exit(1) sys.exit(1)
def compute_pitch(self, layer_stack): def compute_pitch(self, layer, preferred=True):
""" """
This is contact direction independent pitch, This is the preferred direction pitch
i.e. we take the maximum contact dimension 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 (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": if preferred:
contact1 = getattr(contact, layer1 + "_contact") if self.get_preferred_direction(layer1) == "V":
contact_width = contact1.first_layer_width
else:
contact_width = contact1.first_layer_height
else: else:
contact1 = getattr(contact, layer1 + "_via") if self.get_preferred_direction(layer1) == "V":
max_contact = max(contact1.width, contact1.height) contact_width = contact1.first_layer_height
else:
layer1_space = getattr(self, layer1 + "_space") contact_width = contact1.first_layer_width
layer2_space = getattr(self, layer2 + "_space") layer_space = getattr(self, layer1 + "_space")
pitch = max_contact + max(layer1_space, layer2_space)
return pitch #print(layer_stack)
#print(contact1)
pitch = contact_width + layer_space
return round_to_grid(pitch)
def setup_drc_constants(self): def setup_drc_constants(self):
""" """
These are some DRC constants used in many places These are some DRC constants used in many places

View File

@ -12,6 +12,7 @@ import debug
from math import sqrt from math import sqrt
from tech import drc, GDS from tech import drc, GDS
from tech import layer as techlayer from tech import layer as techlayer
from tech import layer_indices
from tech import layer_stacks from tech import layer_stacks
import os import os
from globals import OPTS from globals import OPTS
@ -47,11 +48,7 @@ class layout():
self.pwr_grid_layer = power_grid[0] self.pwr_grid_layer = power_grid[0]
except ImportError: except ImportError:
self.pwr_grid_layer = "m3" 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, path=coordinates,
layer_widths=layer_widths) 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. """Connects a routing path on given layer,coordinates,width.
The layers are the (horizontal, via, vertical). """ The layers are the (horizontal, via, vertical). """
import wire import wire
@ -459,7 +489,8 @@ class layout():
# into rectangles and contacts # into rectangles and contacts
wire.wire(obj=self, wire.wire(obj=self,
layer_stack=layers, layer_stack=layers,
position_list=coordinates) position_list=coordinates,
widen_short_wires=widen_short_wires)
def get_preferred_direction(self, layer): def get_preferred_direction(self, layer):
""" Return the preferred routing directions """ """ 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): def add_via(self, layers, offset, size=[1, 1], directions=None, implant_type=None, well_type=None):
""" Add a three layer via structure. """ """ 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 from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
@ -498,16 +519,6 @@ class layout():
Add a three layer via structure by the center coordinate Add a three layer via structure by the center coordinate
accounting for mirroring and rotation. 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 from sram_factory import factory
via = factory.create(module_type="contact", via = factory.create(module_type="contact",
layer_stack=layers, layer_stack=layers,
@ -547,13 +558,6 @@ class layout():
implant_type=implant_type, implant_type=implant_type,
well_type=well_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, def add_via_stack_center(self,
offset, offset,
from_layer, from_layer,
@ -586,8 +590,8 @@ class layout():
if from_layer == to_layer: if from_layer == to_layer:
return last_via return last_via
from_id = self.get_layer_index(from_layer) from_id = layer_indices[from_layer]
to_id = self.get_layer_index(to_layer) to_id = layer_indices[to_layer]
if from_id < to_id: # grow the stack up if from_id < to_id: # grow the stack up
search_id = 0 search_id = 0
@ -608,24 +612,13 @@ class layout():
implant_type=implant_type, implant_type=implant_type,
well_type=well_type) well_type=well_type)
if from_layer == "active": via = self.__add_via_stack_internal(offset=offset,
via = self.__add_via_stack_internal(offset=offset, directions=directions,
directions=directions, from_layer=curr_stack[next_id],
from_layer=curr_stack[next_id], to_layer=to_layer,
to_layer=to_layer, via_func=via_func,
via_func=via_func, last_via=via,
last_via=via, size=size)
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)
return via return via
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
@ -781,47 +774,47 @@ class layout():
return blockages 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. """ """ Create a horizontal bus of pins. """
return self.create_bus(layer, return self.create_bus(layer,
pitch,
offset, offset,
names, names,
length, length,
vertical=False, 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. """ """ Create a horizontal bus of pins. """
return self.create_bus(layer, return self.create_bus(layer,
pitch,
offset, offset,
names, names,
length, length,
vertical=True, 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. """ """ Create a horizontal bus. """
return self.create_bus(layer, return self.create_bus(layer,
pitch,
offset, offset,
names, names,
length, length,
vertical=True, 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. """ """ Create a horizontal bus. """
return self.create_bus(layer, return self.create_bus(layer,
pitch,
offset, offset,
names, names,
length, length,
vertical=False, 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 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. 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 so we can return the center line offsets
half_minwidth = 0.5 * drc["minwidth_{}".format(layer)] half_minwidth = 0.5 * drc["minwidth_{}".format(layer)]
if not pitch:
pitch = getattr(self, "{}_pitch".format(layer))
line_positions = {} line_positions = {}
if vertical: if vertical:
for i in range(len(names)): for i in range(len(names)):
line_offset = offset + vector(i * pitch, 0) line_offset = offset + vector(i * pitch,
0)
if make_pins: if make_pins:
self.add_layout_pin(text=names[i], self.add_layout_pin(text=names[i],
layer=layer, layer=layer,
@ -900,8 +896,10 @@ class layout():
# left/right then up/down # left/right then up/down
mid_pos = vector(bus_pos.x, pin_pos.y) 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, 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 # Connect to the pin on the instances with a via if it is
# not on the right layer # not on the right layer
@ -918,33 +916,76 @@ class layout():
offset=bus_pos, offset=bus_pos,
rotate=90) 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): def get_layer_pitch(self, layer):
""" Return the track pitch on a given layer """ """ Return the track pitch on a given layer """
if layer == "m1": try:
return (self.m1_pitch, # FIXME: Using non-pref pitch here due to overlap bug in VCG constraints.
self.m1_pitch - self.m1_space, # It should just result in inefficient channel width but will work.
self.m1_space) pitch = getattr(self, "{}_pitch".format(layer))
elif layer == "m2": nonpref_pitch = getattr(self, "{}_nonpref_pitch".format(layer))
return (self.m2_pitch, space = getattr(self, "{}_space".format(layer))
self.m2_pitch - self.m2_space, except AttributeError:
self.m2_space) debug.error("Cannot find layer pitch.", -1)
elif layer == "m3": return (nonpref_pitch, pitch, pitch - space, space)
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.")
def add_horizontal_trunk_route(self, def add_horizontal_trunk_route(self,
pins, pins,
trunk_offset, trunk_offset,
@ -958,7 +999,7 @@ class layout():
min_x = min([pin.center().x for pin in pins]) 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 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)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.vertical_layer)]
# Add the horizontal trunk on the vertical layer! # Add the horizontal trunk on the vertical layer!
@ -1027,14 +1068,15 @@ class layout():
def create_channel_route(self, netlist, def create_channel_route(self, netlist,
offset, offset,
layer_stack, layer_stack,
layer_dirs=None, directions=None,
vertical=False): vertical=False):
""" """
The net list is a list of the nets. Each net is a list of pins The net list is a list of the nets with each net being a list of pins
to be connected. Offset is the lower-left of where the to be connected. The offset is the lower-left of where the
routing channel will start. This does NOT try to minimize the routing channel will start. This does NOT try to minimize the
number of tracks -- instead, it picks an order to avoid 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): def remove_net_from_graph(pin, g):
@ -1051,12 +1093,17 @@ class layout():
g[other_pin]=conflicts g[other_pin]=conflicts
return g 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 Check all the pin pairs on two nets and return a pin
overlap if any pin overlaps. overlap if any pin overlaps.
""" """
if vertical:
pitch = self.horizontal_nonpref_pitch
else:
pitch = self.vertical_nonpref_pitch
for pin1 in net1: for pin1 in net1:
for pin2 in net2: for pin2 in net2:
if vcg_pin_overlap(pin1, pin2, vertical, pitch): 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. # FIXME: If the pins are not in a row, this may break.
# However, a top pin shouldn't overlap another top pin, # However, a top pin shouldn't overlap another top pin,
# for example, so the # for example, so the extra comparison *shouldn't* matter.
# extra comparison *shouldn't* matter.
# Pin 1 must be in the "BOTTOM" set # Pin 1 must be in the "BOTTOM" set
x_overlap = pin1.by() < pin2.by() and abs(pin1.center().x - pin2.center().x) < pitch 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) overlaps = (not vertical and x_overlap) or (vertical and y_overlap)
return overlaps return overlaps
if not layer_dirs: if not directions:
# Use the preferred layer directions # Use the preferred layer directions
if self.get_preferred_direction(layer_stack[0]) == "V": if self.get_preferred_direction(layer_stack[0]) == "V":
self.vertical_layer = layer_stack[0] self.vertical_layer = layer_stack[0]
@ -1091,8 +1137,8 @@ class layout():
else: else:
# Use the layer directions specified to the router rather than # Use the layer directions specified to the router rather than
# the preferred directions # the preferred directions
debug.check(layer_dirs[0] != layer_dirs[1], "Must have unique layer directions.") debug.check(directions[0] != directions[1], "Must have unique layer directions.")
if layer_dirs[0] == "V": if directions[0] == "V":
self.vertical_layer = layer_stack[0] self.vertical_layer = layer_stack[0]
self.horizontal_layer = layer_stack[2] self.horizontal_layer = layer_stack[2]
else: else:
@ -1100,9 +1146,10 @@ class layout():
self.vertical_layer = layer_stack[2] self.vertical_layer = layer_stack[2]
layer_stuff = self.get_layer_pitch(self.vertical_layer) 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) 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 # FIXME: Must extend this to a horizontal conflict graph
# too if we want to minimize the # too if we want to minimize the
@ -1122,6 +1169,10 @@ class layout():
index += 1 index += 1
nets[net_name] = pin_list 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 # Find the vertical pin conflicts
# FIXME: O(n^2) but who cares for now # FIXME: O(n^2) but who cares for now
for net_name1 in nets: for net_name1 in nets:
@ -1133,21 +1184,15 @@ class layout():
# Skip yourself # Skip yourself
if net_name1 == net_name2: if net_name1 == net_name2:
continue continue
if vertical and vcg_nets_overlap(nets[net_name1], if vcg_nets_overlap(nets[net_name1],
nets[net_name2], nets[net_name2],
vertical, 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):
vcg[net_name2].append(net_name1) vcg[net_name2].append(net_name1)
# list of routes to do # list of routes to do
while vcg: while vcg:
# from pprint import pformat # from pprint import pformat
# print("VCG:\n",pformat(vcg)) # print("VCG:\n", pformat(vcg))
# get a route from conflict graph with empty fanout set # get a route from conflict graph with empty fanout set
net_name = None net_name = None
for net_name, conflicts in vcg.items(): for net_name, conflicts in vcg.items():
@ -1171,26 +1216,28 @@ class layout():
self.add_vertical_trunk_route(pin_list, self.add_vertical_trunk_route(pin_list,
offset, offset,
layer_stack, layer_stack,
self.vertical_pitch) self.vertical_nonpref_pitch)
offset += vector(self.vertical_pitch, 0) # This accounts for the via-to-via spacings
offset += vector(self.horizontal_nonpref_pitch, 0)
else: else:
self.add_horizontal_trunk_route(pin_list, self.add_horizontal_trunk_route(pin_list,
offset, offset,
layer_stack, layer_stack,
self.horizontal_pitch) self.horizontal_nonpref_pitch)
offset += vector(0, self.horizontal_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 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 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): def add_boundary(self, ll=vector(0, 0), ur=None):
""" Add boundary for debugging dimensions """ """ Add boundary for debugging dimensions """

View File

@ -19,11 +19,14 @@ class wire(wire_path):
not, it will always go down first. not, it will always go down first.
The points are the center of the wire. The points are the center of the wire.
The layer stack is the vertical, contact/via, and horizontal layers, respectively. 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.obj = obj
self.layer_stack = layer_stack self.layer_stack = layer_stack
self.position_list = position_list self.position_list = position_list
self.widen_short_wires = widen_short_wires
self.pins = [] # used for matching parm lengths self.pins = [] # used for matching parm lengths
self.switch_pos_list = [] self.switch_pos_list = []
@ -114,7 +117,7 @@ class wire(wire_path):
line_length = pl[index + 1][0] - pl[index][0] line_length = pl[index + 1][0] - pl[index][0]
# Make the wire wider to avoid via-to-via spacing problems # Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via # 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 width = self.horiz_layer_contact_width
else: else:
width = self.horiz_layer_width width = self.horiz_layer_width
@ -134,7 +137,7 @@ class wire(wire_path):
line_length = pl[index + 1][1] - pl[index][1] line_length = pl[index + 1][1] - pl[index][1]
# Make the wire wider to avoid via-to-via spacing problems # Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via # 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 width = self.vert_layer_contact_width
else: else:
width = self.vert_layer_width 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[index][0], pl[index + 1][1]))
my_list.append(vector(pl[-1])) my_list.append(vector(pl[-1]))
return my_list return my_list
class wire_path(): class wire_path():
""" """
Object metal wire_path; given the layer type 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 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. 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. If width is not given, it uses minimum layer width.
@ -37,7 +38,7 @@ class wire_path():
self.obj = obj self.obj = obj
self.layer_name = layer self.layer_name = layer
self.layer_id = techlayer[layer] self.layer_id = techlayer[layer]
if width==None: if width == None:
self.layer_width = drc["minwidth_{0}".format(layer)] self.layer_width = drc["minwidth_{0}".format(layer)]
else: else:
self.layer_width = width self.layer_width = width
@ -46,7 +47,6 @@ class wire_path():
self.switch_pos_list = [] self.switch_pos_list = []
self.create_layout() self.create_layout()
def create_layout(self): def create_layout(self):
self.create_rectilinear() self.create_rectilinear()
self.connect_corner() self.connect_corner()
@ -60,9 +60,9 @@ class wire_path():
def connect_corner(self): def connect_corner(self):
""" Add a corner square at every corner of the wire_path.""" """ Add a corner square at every corner of the wire_path."""
from itertools import tee,islice from itertools import tee, islice
nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n)))) nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
threewise=nwise(self.position_list,3) threewise=nwise(self.position_list, 3)
for (a, offset, c) in list(threewise): for (a, offset, c) in list(threewise):
# add a exceptions to prevent a corner when we retrace back in the same direction # 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] offset[1] - 0.5 * self.layer_width]
self.draw_corner_wire(corner_offset) self.draw_corner_wire(corner_offset)
def draw_corner_wire(self, offset): def draw_corner_wire(self, offset):
""" This function adds the corner squares since the center """ This function adds the corner squares since the center
line convention only draws to the center of the corner.""" 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): 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 (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.pin_map = bitcell.pin_map
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_all_wl_names(self): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
if props.bitcell.split_wl: if props.bitcell.split_wl:

View File

@ -264,7 +264,7 @@ class pbitcell(bitcell_base.bitcell_base):
+ 2 * self.implant_enclose_active \ + 2 * self.implant_enclose_active \
+ 0.5*(self.inverter_pmos.active_contact.height - self.m1_width) + 0.5*(self.inverter_pmos.active_contact.height - self.m1_width)
metal1_constraint = max(inverter_pmos_contact_extension, 0) + self.m1_space 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 # read port dimensions
width_reduction = self.read_nmos.active_width - self.read_nmos.get_pin("D").cx() 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 Calculate positions that describe the edges
and dimensions of the cell 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.topmost_ypos = self.inverter_nmos_ypos \
+ self.inverter_nmos.active_height \ + self.inverter_nmos.active_height \
+ self.inverter_gap \ + self.inverter_gap \
@ -378,14 +378,15 @@ class pbitcell(bitcell_base.bitcell_base):
+ 0.5 * contact.poly_contact.height, + 0.5 * contact.poly_contact.height,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack, 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 \ contact_offset_right = vector(self.inverter_nmos_right.get_pin("S").lc().x \
- 0.5*contact.poly_contact.height, - 0.5*contact.poly_contact.height,
self.cross_couple_lower_ypos) self.cross_couple_lower_ypos)
self.add_via_center(layers=self.poly_stack, 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) # connect contacts to gate poly (cross couple connections)
gate_offset_right = vector(self.inverter_nmos_right.get_pin("G").lc().x, 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): def route_rails(self):
""" Adds gnd and vdd rails and connects them to the inverters """ """ Adds gnd and vdd rails and connects them to the inverters """
# Add rails for vdd and gnd # 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.gnd_position = vector(0, gnd_ypos)
self.add_rect_center(layer="m1", self.add_rect_center(layer="m1",
offset=self.gnd_position, offset=self.gnd_position,
width=self.width) 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 \ vdd_ypos = self.inverter_nmos_ypos \
@ -416,7 +417,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.add_rect_center(layer="m1", self.add_rect_center(layer="m1",
offset=self.vdd_position, offset=self.vdd_position,
width=self.width) 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): def create_readwrite_ports(self):
""" """
@ -483,7 +484,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.port_ypos]) self.port_ypos])
# add pin for RWWL # 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.rwwl_positions[k] = vector(0, rwwl_ypos)
self.add_layout_pin_rect_center(text=self.rw_wl_names[k], self.add_layout_pin_rect_center(text=self.rw_wl_names[k],
layer="m1", layer="m1",
@ -579,8 +580,8 @@ class pbitcell(bitcell_base.bitcell_base):
# add pin for WWL # add pin for WWL
wwl_ypos = rwwl_ypos = self.m1_offset \ wwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_rw_ports * self.m1_pitch \ - self.num_rw_ports * self.m1_nonpref_pitch \
- k * self.m1_pitch - k * self.m1_nonpref_pitch
self.wwl_positions[k] = vector(0, wwl_ypos) self.wwl_positions[k] = vector(0, wwl_ypos)
self.add_layout_pin_rect_center(text=self.w_wl_names[k], self.add_layout_pin_rect_center(text=self.w_wl_names[k],
layer="m1", layer="m1",
@ -705,9 +706,9 @@ class pbitcell(bitcell_base.bitcell_base):
# add pin for RWL # add pin for RWL
rwl_ypos = rwwl_ypos = self.m1_offset \ rwl_ypos = rwwl_ypos = self.m1_offset \
- self.num_rw_ports * self.m1_pitch \ - self.num_rw_ports * self.m1_nonpref_pitch \
- self.num_w_ports * self.m1_pitch \ - self.num_w_ports * self.m1_nonpref_pitch \
- k * self.m1_pitch - k * self.m1_nonpref_pitch
self.rwl_positions[k] = vector(0, rwl_ypos) self.rwl_positions[k] = vector(0, rwl_ypos)
self.add_layout_pin_rect_center(text=self.r_wl_names[k], self.add_layout_pin_rect_center(text=self.r_wl_names[k],
layer="m1", layer="m1",
@ -771,6 +772,7 @@ class pbitcell(bitcell_base.bitcell_base):
self.add_via_center(layers=self.poly_stack, self.add_via_center(layers=self.poly_stack,
offset=port_contact_offset) offset=port_contact_offset)
self.add_path("poly", [gate_offset, port_contact_offset]) self.add_path("poly", [gate_offset, port_contact_offset])
self.add_path("m1", self.add_path("m1",
[port_contact_offset, wl_contact_offset]) [port_contact_offset, wl_contact_offset])
@ -821,7 +823,8 @@ class pbitcell(bitcell_base.bitcell_base):
# Leave bitline disconnected if a dummy cell # Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell: if not self.dummy_bitcell:
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest) offset=port_contact_offest,
directions="nonpref")
self.add_path("m2", self.add_path("m2",
[port_contact_offest, bl_offset], width=contact.m1_via.height) [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 # Leave bitline disconnected if a dummy cell
if not self.dummy_bitcell: if not self.dummy_bitcell:
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=port_contact_offest) offset=port_contact_offest,
directions="nonpref")
self.add_path("m2", self.add_path("m2",
[port_contact_offest, br_offset], width=contact.m1_via.height) [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: for position in nmos_contact_positions:
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=position) offset=position,
directions=("V", "V"))
if position.x > 0: if position.x > 0:
contact_correct = 0.5 * contact.m1_via.height 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, supply_offset = vector(position.x + contact_correct,
self.gnd_position.y) self.gnd_position.y)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=supply_offset) offset=supply_offset,
directions=("H", "H"))
self.add_path("m2", [position, supply_offset]) 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.poly_to_contact - 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack, 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 \ right_storage_contact = vector(self.inverter_nmos_right.get_pin("G").rc().x \
+ self.poly_to_contact + 0.5*contact.poly_contact.width, + self.poly_to_contact + 0.5*contact.poly_contact.width,
self.cross_couple_upper_ypos) self.cross_couple_upper_ypos)
self.add_via_center(layers=self.poly_stack, 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) 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]) 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 \ well_height = self.vdd_position.y - inverter_well_ypos \
+ self.nwell_enclose_active + drc["minwidth_tx"] + self.nwell_enclose_active + drc["minwidth_tx"]
# FIXME fudge factor xpos offset = [inverter_well_xpos, inverter_well_ypos]
offset = [inverter_well_xpos + 2*self.nwell_enclose_active, inverter_well_ypos]
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=offset, offset=offset,
width=well_width, 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") self.add_pin(self.bitcell_array.get_rbl_bl_name(self.port_rbl_map[port]), "OUTPUT")
for port in self.write_ports: for port in self.write_ports:
for bit in range(self.word_size): 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 port in self.all_ports:
for bit in range(self.addr_size): 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 # For more than one bank, we have a bank select and name
# the signals gated_*. # the signals gated_*.
@ -128,16 +128,14 @@ class bank(design.design):
bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name) bl_pin = self.bitcell_array_inst.get_pin(bl_pin_name)
# This will ensure the pin is only on the top or bottom edge # This will ensure the pin is only on the top or bottom edge
if port % 2: 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) left_right_offset = vector(self.max_x_offset, via_offset.y)
else: 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) left_right_offset = vector(self.min_x_offset, via_offset.y)
if bl_pin == "m1": self.add_via_stack_center(from_layer=bl_pin.layer,
self.add_via_center(layers=self.m1_stack, to_layer="m3",
offset=via_offset) offset=via_offset)
self.add_via_center(layers=self.m2_stack,
offset=via_offset)
self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port), self.add_layout_pin_segment_center(text="rbl_bl{0}".format(port),
layer="m3", layer="m3",
start=left_right_offset, start=left_right_offset,
@ -635,11 +633,10 @@ class bank(design.design):
# Port 0 # Port 0
# The bank is at (0,0), so this is to the left of the y-axis. # 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 # 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 # 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 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", self.bus_xoffset[0] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset, offset=control_bus_offset,
names=self.control_signals[0], names=self.control_signals[0],
length=control_bus_length, length=control_bus_length,
@ -650,11 +647,10 @@ class bank(design.design):
if len(self.all_ports)==2: if len(self.all_ports)==2:
# The other control bus is routed up to two pitches above the bitcell array # 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_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) 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 # 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", self.bus_xoffset[1] = self.create_bus(layer="m2",
pitch=self.m2_pitch,
offset=control_bus_offset, offset=control_bus_offset,
names=list(reversed(self.control_signals[1])), names=list(reversed(self.control_signals[1])),
length=control_bus_length, 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) self.copy_layout_pin(self.column_decoder_inst[port], decoder_name, addr_name)
if port % 2: 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: 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] 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] 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)) 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): def add_lvs_correspondence_points(self):
""" """
@ -910,30 +908,39 @@ class bank(design.design):
# pre-decoder and this connection is in metal3 # pre-decoder and this connection is in metal3
connection = [] connection = []
connection.append((self.prefix + "p_en_bar{}".format(port), 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]) rbl_wl_name = self.bitcell_array.get_rbl_wl_name(self.port_rbl_map[port])
connection.append((self.prefix + "wl_en{}".format(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 in self.write_ports:
if port % 2: if port % 2:
connection.append((self.prefix + "w_en{}".format(port), 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: else:
connection.append((self.prefix + "w_en{}".format(port), 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: if port in self.read_ports:
connection.append((self.prefix + "s_en{}".format(port), 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: for (control_signal, pin_pos, pin_layer) in connection:
control_mid_pos = self.bus_xoffset[port][control_signal] if port==0:
control_pos = vector(self.bus_xoffset[port][control_signal].x, pin_pos.y) y_offset = self.min_y_offset
self.add_wire(self.m1_stack, [control_mid_pos, control_pos, pin_pos]) else:
self.add_via_center(layers=self.m1_stack, y_offset = self.max_y_offset
offset=control_pos) 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 # clk to wordline_driver
control_signal = self.prefix + "wl_en{}".format(port) 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) 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_path("m2", [bank_sel_line_pos, bank_sel_line_end])
self.add_via_center(layers=self.m1_stack, 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 # Route the pin to the left edge as well
bank_sel_pin_pos=vector(0, 0) bank_sel_pin_pos=vector(0, 0)
@ -242,30 +242,31 @@ class bank_select(design.design):
# Connect the logic output to inverter input # Connect the logic output to inverter input
out_pin = logic_inst.get_pin("Z") 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_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) 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) 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]) self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar # 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) input_pos = vector(xoffset_bank_signal, logic_pos.y)
self.add_path("m2", [logic_pos, input_pos]) self.add_path("m3", [logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(self.m2_stack,
offset=logic_pos, input_pos)
directions=("H", "H")) 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 # 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) input_pos = vector(0, logic_pos.y)
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(from_layer=logic_pin.layer,
offset=logic_pos, to_layer="m3",
directions=("H", "H")) offset=logic_pos)
self.add_via_center(layers=self.m2_stack,
offset=logic_pos,
directions=("H", "H"))
self.add_layout_pin_segment_center(text=input_name, self.add_layout_pin_segment_center(text=input_name,
layer="m3", layer="m3",
start=input_pos, start=input_pos,

View File

@ -384,7 +384,10 @@ class control_logic(design.design):
height = self.control_logic_center.y - self.m2_pitch height = self.control_logic_center.y - self.m2_pitch
offset = vector(self.ctrl_dff_array.width, 0) 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): def create_instances(self):
""" Create all the instances """ """ Create all the instances """
@ -432,7 +435,7 @@ class control_logic(design.design):
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row) self.place_rbl_delay_row(row)
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row) self.place_sen_row(row)
row += 1 row += 1
@ -522,16 +525,8 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=clk_pos) offset=clk_pos)
# Connect this at the bottom of the buffer self.route_output_to_bus_jogged(self.clk_buf_inst,
out_pos = self.clk_buf_inst.get_pin("Z").center() "clk_buf")
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.connect_output(self.clk_buf_inst, "Z", "clk_buf") self.connect_output(self.clk_buf_inst, "Z", "clk_buf")
def create_gated_clk_bar_row(self): 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", self.gated_clk_bar_inst = self.add_inst(name="and2_gated_clk_bar",
mod=self.and2) 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): def place_gated_clk_bar_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
@ -554,31 +549,26 @@ class control_logic(design.design):
def route_gated_clk_bar(self): def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"]) clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center() out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center() in_pos = self.gated_clk_bar_inst.get_pin("A").center()
mid1 = vector(in_pos.x, out_pos.y) self.add_zjog("m1", out_pos, in_pos)
self.add_path("m1", [out_pos, mid1, in_pos])
# This is the second gate over, so it needs to be on M3 # 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.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst, self.gated_clk_bar_inst,
self.rail_offsets, self.rail_offsets,
self.m2_stack[::-1]) self.m2_stack[::-1])
# The pin is on M1, so we need another via as well # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, b_pin = self.gated_clk_bar_inst.get_pin("B")
offset=self.gated_clk_bar_inst.get_pin("A").center()) 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 # This is the second gate over, so it needs to be on M3
clkbuf_map = zip(["Z"], ["gated_clk_bar"]) self.route_output_to_bus_jogged(self.gated_clk_bar_inst,
self.connect_vertical_bus(clkbuf_map, "gated_clk_bar")
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())
def create_gated_clk_buf_row(self): def create_gated_clk_buf_row(self):
self.gated_clk_buf_inst = self.add_inst(name="and2_gated_clk_buf", 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): def route_gated_clk_buf(self):
clkbuf_map = zip(["A", "B"], ["clk_buf", "cs"]) 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"]) clkbuf_map = zip(["Z"], ["gated_clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.connect_vertical_bus(clkbuf_map,
@ -602,8 +594,10 @@ class control_logic(design.design):
self.rail_offsets, self.rail_offsets,
self.m2_stack[::-1]) self.m2_stack[::-1])
# The pin is on M1, so we need another via as well # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, z_pin = self.gated_clk_buf_inst.get_pin("Z")
offset=self.gated_clk_buf_inst.get_pin("Z").center()) self.add_via_stack_center(from_layer=z_pin.layer,
to_layer="m2",
offset=z_pin.center())
def create_wlen_row(self): def create_wlen_row(self):
# input pre_p_en, output: wl_en # 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"]) 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) 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() out_pin = self.p_en_bar_nand_inst.get_pin("Z")
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() out_pos = out_pin.center()
mid1 = vector(out_pos.x, in_pos.y) in_pin = self.p_en_bar_driver_inst.get_pin("A")
self.add_wire(self.m1_stack, [out_pos, mid1, in_pos]) 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") self.connect_output(self.p_en_bar_driver_inst, "Z", "p_en_bar")
def create_sen_row(self): def create_sen_row(self):
@ -704,11 +703,7 @@ class control_logic(design.design):
# Connect from delay line # Connect from delay line
# Connect to rail # Connect to rail
rbl_map = zip(["Z"], ["rbl_bl_delay_bar"]) self.route_output_to_bus_jogged(self.rbl_bl_delay_inv_inst, "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())
rbl_map = zip(["A"], ["rbl_bl_delay"]) rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) 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"]) dff_out_map = zip(["dout_bar_0", "dout_0"], ["cs", "cs_bar"])
else: else:
dff_out_map = zip(["dout_bar_0"], ["cs"]) 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 # 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() 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) 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_wire(self.m1_stack, [in_pos, mid_pos, rail_pos])
self.add_via_center(layers=self.m1_stack, 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.add_path("m1", [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst, "gnd") 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, "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): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ 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) offset = vector(x_offset, y_offset)
inst.place(offset, mirror) inst.place(offset, mirror)
return x_offset + inst.width 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()) height=din_pin.height())
dout_pin = self.inv2_inst.get_pin("Z") 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) q_pos = mid_pos - vector(0, self.m2_pitch)
self.add_layout_pin_rect_center(text="Q", self.add_layout_pin_rect_center(text="Q",
layer="m2", layer="m2",

View File

@ -12,7 +12,7 @@ from sram_factory import factory
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from errors import drc_error from errors import drc_error
from tech import cell_properties from tech import cell_properties, layer
class hierarchical_decoder(design.design): class hierarchical_decoder(design.design):
@ -89,6 +89,7 @@ class hierarchical_decoder(design.design):
self.place_pre_decoder() self.place_pre_decoder()
self.place_row_decoder() self.place_row_decoder()
self.route_inputs() self.route_inputs()
self.route_outputs()
self.route_decoder_bus() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_all_coordinates()
@ -103,7 +104,7 @@ class hierarchical_decoder(design.design):
height=self.cell_height) height=self.cell_height)
self.add_mod(self.and2) self.add_mod(self.and2)
self.and3 = factory.create(module_type="pand3", self.and3 = factory.create(module_type="pand3",
height=self.cell_height) height=self.cell_height)
self.add_mod(self.and3) self.add_mod(self.and3)
self.add_decoders() self.add_decoders()
@ -186,6 +187,13 @@ class hierarchical_decoder(design.design):
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple) self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
# We will place this many final decoders per row # We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows) 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 # Calculates height and width of row-decoder
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
@ -194,20 +202,21 @@ class hierarchical_decoder(design.design):
else: else:
nand_width = self.and3.width nand_width = self.and3.width
nand_inputs = 3 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 self.row_decoder_height = self.inv.height * self.num_rows
decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m3_pitch decoder_input_wire_height = self.decoders_per_row * nand_inputs * self.m2_pitch
#print(self.decoders_per_row, nand_inputs) # print(self.decoders_per_row, nand_inputs)
#print(decoder_input_wire_height, self.cell_height) # print(decoder_input_wire_height, self.cell_height)
if 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.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 # Calculates height and width of hierarchical decoder
# Add extra pitch for good measure # 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.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width \ + self.internal_routing_width \
+ self.decoders_per_row * nand_width + self.inv.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_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_bus = self.create_vertical_pin_bus(layer="m3", self.input_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m3_pitch,
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=input_height) length=input_height)
@ -276,13 +284,11 @@ class hierarchical_decoder(design.design):
self.add_via_stack_center(from_layer="m2", self.add_via_stack_center(from_layer="m2",
to_layer="m3", to_layer="m3",
offset=input_offset, offset=input_offset)
directions="nonpref")
self.add_via_stack_center(from_layer="m2", self.add_via_stack_center(from_layer="m2",
to_layer="m3", to_layer="m3",
offset=output_offset, offset=output_offset)
directions="nonpref") self.add_path("m3", [input_offset, output_offset])
self.add_path(("m2"), [input_offset, output_offset])
def add_pins(self): def add_pins(self):
""" Add the module pins """ """ Add the module pins """
@ -424,7 +430,7 @@ class hierarchical_decoder(design.design):
""" """
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
self.place_decoder_and_array() self.place_decoder_and_array()
self.route_decoder()
def place_decoder_and_array(self): 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), self.and_inst[inst_index].place(offset=vector(x_off, y_off),
mirror=mirror) mirror=mirror)
def route_decoder(self): def route_outputs(self):
""" Add the pins. """ """ Add the pins. """
for output in range(self.num_outputs): max_xoffset = max(x.rx() for x in self.and_inst)
z_pin = self.and_inst[output].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(output), for output_index in range(self.num_outputs):
layer="m1", row_remainder = (output_index % self.decoders_per_row)
offset=z_pin.ll(),
width=z_pin.width(), and_inst = self.and_inst[output_index]
height=z_pin.height()) 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): def route_decoder_bus(self):
""" """
@ -480,8 +498,8 @@ class hierarchical_decoder(design.design):
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
# This leaves an offset for the predecoder output jogs # 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)] 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", self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m3_pitch, pitch=self.decoder_bus_pitch,
offset=vector(0, 0), offset=vector(0, 0),
names=input_bus_names, names=input_bus_names,
length=self.height) length=self.height)
@ -525,22 +543,27 @@ class hierarchical_decoder(design.design):
""" """
output_index = 0 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): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (output_index < self.num_outputs): 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) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_bus_outputs(predecode_name, self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"), self.and_inst[output_index].get_pin("A"),
row_offset) output_index,
0)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_bus_outputs(predecode_name, self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"), self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch) output_index,
1)
output_index = output_index + 1 output_index = output_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
@ -549,21 +572,21 @@ class hierarchical_decoder(design.design):
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
# FIXME: convert to connect_bus? # FIXME: convert to connect_bus?
if (output_index < self.num_outputs): 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) predecode_name = "predecode_{}".format(index_A)
self.route_predecode_bus_outputs(predecode_name, self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"), self.and_inst[output_index].get_pin("A"),
row_offset) output_index,
0)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_bus_outputs(predecode_name, self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"), self.and_inst[output_index].get_pin("B"),
row_offset + self.m2_pitch) output_index,
1)
predecode_name = "predecode_{}".format(index_C) predecode_name = "predecode_{}".format(index_C)
self.route_predecode_bus_outputs(predecode_name, self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("C"), self.and_inst[output_index].get_pin("C"),
row_offset + 2 * self.m2_pitch) output_index,
2)
output_index = output_index + 1 output_index = output_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -583,47 +606,56 @@ class hierarchical_decoder(design.design):
# The nand and inv are the same height rows... # The nand and inv are the same height rows...
supply_pin = self.and_inst[num].get_pin(pin_name) supply_pin = self.and_inst[num].get_pin(pin_name)
pin_pos = vector(xoffset, supply_pin.cy()) 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())]) [supply_pin.lc(), vector(xoffset, supply_pin.cy())])
self.add_power_pin(name=pin_name, self.add_power_pin(name=pin_name,
loc=pin_pos) loc=pin_pos,
start_layer=supply_pin.layer)
# Copy the pins from the predecoders # Copy the pins from the predecoders
for pre in self.pre2x4_inst + self.pre3x8_inst: for pre in self.pre2x4_inst + self.pre3x8_inst:
self.copy_layout_pin(pre, "vdd") self.copy_layout_pin(pre, "vdd")
self.copy_layout_pin(pre, "gnd") 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 Connect the routing rail to the given metal1 pin
using a routing track at the given y_offset 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): def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
""" """
Connect the routing rail to the given metal1 pin using a jog 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. # This routes the pin up to the rail, basically, to avoid conflicts.
# It would be fixed with a channel router. # 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() pin_pos = pin.center()
mid_point1 = vector(x_offset, pin_pos.y) rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
mid_point2 = vector(x_offset, pin_pos.y + self.inv.height / 2) self.add_path("m1", [pin_pos, rail_pos])
rail_pos = vector(self.predecode_bus[rail_name].x, mid_point2.y) self.add_via_stack_center(from_layer=pin.layer,
self.add_path("m1", [pin_pos, mid_point1, mid_point2, rail_pos]) to_layer="m1",
offset=pin_pos)
self.add_via_stack_center(from_layer="m1", self.add_via_stack_center(from_layer="m1",
to_layer="m3", to_layer="m2",
offset=rail_pos, offset=rail_pos)
directions="nonpref")
def input_load(self): def input_load(self):
if self.determine_predecodes(self.num_inputs)[1]==0: if self.determine_predecodes(self.num_inputs)[1]==0:

View File

@ -11,6 +11,7 @@ import math
import contact import contact
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from tech import cell_properties
class hierarchical_predecode(design.design): class hierarchical_predecode(design.design):
@ -19,7 +20,17 @@ class hierarchical_predecode(design.design):
""" """
def __init__(self, name, input_number, height=None): def __init__(self, name, input_number, height=None):
self.number_of_inputs = input_number 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)) self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
design.design.__init__(self, name) design.design.__init__(self, name)
@ -57,10 +68,10 @@ class hierarchical_predecode(design.design):
self.height = self.number_of_outputs * self.and_mod.height self.height = self.number_of_outputs * self.and_mod.height
# x offset for input inverters # 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 # 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 # x offset to output inverters
self.width = self.x_off_and + self.and_mod.width self.width = self.x_off_and + self.and_mod.width
@ -68,9 +79,8 @@ class hierarchical_predecode(design.design):
def route_rails(self): def route_rails(self):
""" Create all of the rails for the inputs and vdd/gnd/inputs_bar/inputs """ """ 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)] input_names = ["in_{}".format(x) for x in range(self.number_of_inputs)]
offset = vector(0.5 * self.m3_width, self.m1_pitch) offset = vector(0.5 * self.m2_width, self.m3_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m3", self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m3_pitch,
offset=offset, offset=offset,
names=input_names, names=input_names,
length=self.height - 2 * self.m1_pitch) 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)] 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)] non_invert_names = ["A_{}".format(x) for x in range(self.number_of_inputs)]
decode_names = invert_names + non_invert_names decode_names = invert_names + non_invert_names
offset = vector(self.x_off_inv_1 + self.inv.width + 2 * self.m3_pitch, self.m1_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="m3", self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m3_pitch,
offset=offset, offset=offset,
names=decode_names, names=decode_names,
length=self.height - 2 * self.m1_pitch) length=self.height - 2 * self.m1_pitch)
@ -151,11 +160,13 @@ class hierarchical_predecode(design.design):
a_pin = "A_{}".format(num) a_pin = "A_{}".format(num)
in_pos = vector(self.input_rails[in_pin].x, y_offset) in_pos = vector(self.input_rails[in_pin].x, y_offset)
a_pos = vector(self.decode_rails[a_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_path("m1", [in_pos, a_pos])
self.add_via_center(layers=self.m2_stack, self.add_via_stack_center(from_layer="m1",
offset=[self.input_rails[in_pin].x, y_offset]) to_layer="m2",
self.add_via_center(layers=self.m2_stack, offset=[self.input_rails[in_pin].x, y_offset])
offset=[self.decode_rails[a_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): 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) 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_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, self.add_via_stack_center(from_layer=inv_out_pin.layer,
to_layer="m3", to_layer="m2",
offset=rail_pos, offset=rail_pos)
directions="nonpref")
# route input # route input
pin = self.in_inst[inv_num].get_pin("A") pin = self.in_inst[inv_num].get_pin("A")
inv_in_pos = pin.lc() inv_in_pos = pin.lc()
in_pos = vector(self.input_rails[in_pin].x, inv_in_pos.y) 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, 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", to_layer="m2",
offset=inv_in_pos, offset=in_pos)
directions="nonpref")
self.add_via_center(layers=self.m2_stack,
offset=in_pos)
def route_and_to_rails(self): def route_and_to_rails(self):
# This 2D array defines the connection mapping # 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 = self.and_inst[k].get_pin(gate_pin)
pin_pos = pin.center() pin_pos = pin.center()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m2", [rail_pos, pin_pos]) self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m2_stack, self.add_via_stack_center(from_layer="m1",
offset=rail_pos,
directions="nonpref")
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m2", 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, offset=pin_pos,
directions=("H", "H")) directions=direction)
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Add a pin for each row of vdd/gnd which are must-connects next level up. """ """ 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"]: for n in ["vdd", "gnd"]:
and_pin = self.and_inst[num].get_pin(n) and_pin = self.and_inst[num].get_pin(n)
supply_offset = and_pin.ll().scale(0, 1) supply_offset = and_pin.ll().scale(0, 1)
self.add_rect(layer="m1", self.add_rect(layer=and_pin.layer,
offset=supply_offset, offset=supply_offset,
width=self.and_inst[num].rx()) width=self.and_inst[num].rx())
# Add pins in two locations # Add pins in two locations
for xoffset in [in_xoffset]: for xoffset in [in_xoffset]:
pin_pos = vector(xoffset, and_pin.cy()) 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 # Copyright (c) 2016-2019 Regents of the University of California
# All rights reserved. # All rights reserved.
# #
import sys
from tech import drc, parameter
from math import log from math import log
import debug import debug
import design import design
@ -13,6 +11,7 @@ from vector import vector
from globals import OPTS from globals import OPTS
class port_address(design.design): class port_address(design.design):
""" """
Create the address port (row decoder and wordline driver).. 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)) self.addr_size = int(log(self.num_rows, 2))
if name == "": if name == "":
name = "port_address_{0}_{1}".format(cols,rows) name = "port_address_{0}_{1}".format(cols, rows)
design.design.__init__(self, name) 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() self.create_netlist()
if not OPTS.netlist_only: 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.create_layout()
self.add_boundary() self.add_boundary()
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
self.add_modules() self.add_modules()
@ -51,16 +49,15 @@ class port_address(design.design):
""" Adding pins for port address module""" """ Adding pins for port address module"""
for bit in range(self.addr_size): 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") self.add_pin("wl_en", "INPUT")
for bit in range(self.num_rows): 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("vdd", "POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd", "GROUND")
def route_layout(self): def route_layout(self):
""" Create routing amoung the modules """ """ Create routing amoung the modules """
@ -71,8 +68,8 @@ class port_address(design.design):
def route_supplies(self): def route_supplies(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
for inst in self.insts: for inst in self.insts:
self.copy_power_pins(inst,"vdd") self.copy_power_pins(inst, "vdd")
self.copy_power_pins(inst,"gnd") self.copy_power_pins(inst, "gnd")
def route_pins(self): def route_pins(self):
for row in range(self.addr_size): for row in range(self.addr_size):
@ -119,12 +116,10 @@ class port_address(design.design):
temp.extend(["vdd", "gnd"]) temp.extend(["vdd", "gnd"])
self.connect_inst(temp) self.connect_inst(temp)
def create_wordline_driver(self): def create_wordline_driver(self):
""" Create the Wordline Driver """ """ 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) mod=self.wordline_driver)
temp = [] temp = []
@ -137,19 +132,13 @@ class port_address(design.design):
temp.append("gnd") temp.append("gnd")
self.connect_inst(temp) self.connect_inst(temp)
def place_instances(self): def place_instances(self):
""" """
Compute the offsets and place the instances. Compute the offsets and place the instances.
""" """
# A space for wells or jogging m2 row_decoder_offset = vector(0, 0)
self.m2_gap = max(2*drc("pwell_to_nwell") + drc("nwell_enclose_active"), wordline_driver_offset = vector(self.row_decoder.width, 0)
3*self.m2_pitch)
row_decoder_offset = vector(0,0)
wordline_driver_offset = vector(self.row_decoder.width + self.m2_gap,0)
self.wordline_driver_inst.place(wordline_driver_offset) self.wordline_driver_inst.place(wordline_driver_offset)
self.row_decoder_inst.place(row_decoder_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 # 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! # 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): for bit in range(num_bits):
bottom_names = self._get_bitline_pins(bot_inst_group, bit) bottom_names = self._get_bitline_pins(bot_inst_group, bit)
top_names = self._get_bitline_pins(top_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, width=self.width,
height=pin.height()) height=pin.height())
# Replica bitlines # 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] 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) pin = inst.get_pin(pin_name)
if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names: if bl_name in self.rbl_bl_names or bl_name in self.rbl_br_names:
name = bl_name name = bl_name
else: else:
name = "rbl_{0}_{1}".format(pin_name,port) name = "rbl_{0}_{1}".format(pin_name, port)
self.add_layout_pin(text=name, self.add_layout_pin(text=name,
layer=pin.layer, layer=pin.layer,
offset=pin.ll().scale(1,0), offset=pin.ll().scale(1, 0),
width=pin.width(), width=pin.width(),
height=self.height) height=self.height)
for pin_name in ["vdd","gnd"]: for pin_name in ["vdd", "gnd"]:
for inst in self.insts: for inst in self.insts:
pin_list = inst.get_pins(pin_name) pin_list = inst.get_pins(pin_name)
for pin in pin_list: 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): def get_rbl_wl_name(self, port):
""" Return the WL for the given RBL port """ """ Return the WL for the given RBL port """
return self.rbl_wl_names[port] return self.rbl_wl_names[port]
@ -398,7 +396,7 @@ class replica_bitcell_array(design.design):
# Dynamic Power from Bitline # Dynamic Power from Bitline
bl_wire = self.gen_bl_wire() 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 bl_swing = OPTS.rbl_delay_percentage
freq = spice["default_event_frequency"] freq = spice["default_event_frequency"]
bitline_dynamic = self.calc_dynamic_power(corner, cell_load, freq, swing=bl_swing) 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", self.add_power_pin(name="gnd",
loc=gnd_pin.center(), loc=gnd_pin.center(),
start_layer=gnd_pin.layer, start_layer=gnd_pin.layer,
vertical=True) directions=("V", "V"))
vdd_pin = inst.get_pin("vdd") vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd", self.add_power_pin(name="vdd",
loc=vdd_pin.center(), loc=vdd_pin.center(),
start_layer=vdd_pin.layer, start_layer=vdd_pin.layer,
vertical=True) directions=("V", "V"))
bl_pin = inst.get_pin(inst.mod.get_bl_names()) bl_pin = inst.get_pin(inst.mod.get_bl_names())
br_pin = inst.get_pin(inst.mod.get_br_names()) br_pin = inst.get_pin(inst.mod.get_br_names())

View File

@ -8,7 +8,7 @@
import debug import debug
import design import design
import math import math
import contact from tech import drc
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
from globals import OPTS 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) self.decoders_per_row = math.ceil(self.bitcell_rows / self.driver_rows)
def place_drivers(self): 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 self.height = self.and2.height * self.driver_rows
for inst_index in range(self.bitcell_rows): for inst_index in range(self.bitcell_rows):
@ -136,7 +138,7 @@ class wordline_driver(design.design):
y_offset = self.and2.height * row y_offset = self.and2.height * row
inst_mirror = "R0" inst_mirror = "R0"
x_offset = and2_xoffset + dec * self.and2.width x_offset = dec * self.and2.width
and2_offset = [x_offset, y_offset] and2_offset = [x_offset, y_offset]
# add and2 # add and2
@ -147,32 +149,26 @@ class wordline_driver(design.design):
""" Route all of the signals """ """ Route all of the signals """
# Wordline enable connection # 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", en_pin = self.add_layout_pin(text="en",
layer="m2", layer="m2",
offset=en_offset, offset=en_bottom_pos,
width=self.m2_width,
height=self.height) height=self.height)
for inst_index in range(self.bitcell_rows): for inst_index in range(self.bitcell_rows):
and_inst = self.and_inst[inst_index] and_inst = self.and_inst[inst_index]
row = math.floor(inst_index / self.decoders_per_row) 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 # connect the decoder input pin to and2 A
a_pin = and_inst.get_pin("A") a_pin = and_inst.get_pin("A")
a_pos = a_pin.center() a_pos = a_pin.center()
a_offset = vector(clk_offset.x, a_pos.y)
# must under the clk line in M1 # must under the clk line in M1
self.add_layout_pin_segment_center(text="in_{0}".format(row), self.add_layout_pin_segment_center(text="in_{0}".format(row),
layer="m1", layer="m1",

View File

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

View File

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

View File

@ -44,6 +44,7 @@ class pand3(pgate.pgate):
self.place_insts() self.place_insts()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.route_supply_rails()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
@ -81,22 +82,6 @@ class pand3(pgate.pgate):
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()]) [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self): 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") pin = self.inv_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer=pin.layer, layer=pin.layer,

View File

@ -37,6 +37,7 @@ class pbuf(pgate.pgate):
self.place_insts() self.place_insts()
self.add_wires() self.add_wires()
self.add_layout_pins() self.add_layout_pins()
self.route_supply_rails()
self.add_boundary() self.add_boundary()
def add_pins(self): def add_pins(self):
@ -78,26 +79,9 @@ class pbuf(pgate.pgate):
# inv1 Z to inv2 A # inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy()) self.add_zjog(self.route_layer, z1_pin.center(), a2_pin.center())
self.add_path("m1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self): 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") z_pin = self.inv2_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer, layer=z_pin.layer,

View File

@ -76,6 +76,7 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx() self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height self.height = self.inv_inst_list[0].height
self.route_supply_rails()
self.add_boundary() self.add_boundary()
def add_pins(self): def add_pins(self):
@ -146,21 +147,6 @@ class pdriver(pgate.pgate):
a_inst_list[x].center()]) a_inst_list[x].center()])
def add_layout_pins(self): 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") z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",

View File

@ -17,6 +17,7 @@ from globals import OPTS
if(OPTS.tech_name == "s8"): if(OPTS.tech_name == "s8"):
from tech import nmos_bins, pmos_bins, accuracy_requirement from tech import nmos_bins, pmos_bins, accuracy_requirement
class pgate(design.design): class pgate(design.design):
""" """
This is a module that implements some shared This is a module that implements some shared
@ -30,8 +31,8 @@ class pgate(design.design):
if height: if height:
self.height = height self.height = height
elif not height: elif not height:
# By default, we make it 10 M1 pitch tall # By default, something simple
self.height = 10*self.m1_pitch self.height = 14 * self.m1_pitch
if "li" in layer: if "li" in layer:
self.route_layer = "li" 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_width = getattr(self, "{}_width".format(self.route_layer))
self.route_layer_space = getattr(self, "{}_space".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)) 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() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -68,13 +77,14 @@ class pgate(design.design):
height = supply_pin.uy() - source_pin.by() height = supply_pin.uy() - source_pin.by()
else: else:
debug.error("Invalid supply name.", -1) 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, self.add_rect(layer=source_pin.layer,
offset=source_pin.ll(), offset=source_pin.ll(),
height=height, height=height,
width=source_pin.width()) 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. Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or Position specifies to place the contact the left, center, or
@ -100,8 +110,6 @@ class pgate(design.design):
# Center is completely symmetric. # Center is completely symmetric.
contact_width = contact.poly_contact.width 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": if position == "center":
contact_offset = left_gate_offset \ contact_offset = left_gate_offset \
@ -118,18 +126,16 @@ class pgate(design.design):
else: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
if hasattr(self, "li_stack"): via = self.add_via_stack_center(from_layer="poly",
self.add_via_center(layers=self.li_stack, to_layer=self.route_layer,
offset=contact_offset) offset=contact_offset,
directions=directions)
self.add_via_center(layers=self.poly_stack,
offset=contact_offset)
self.add_layout_pin_rect_center(text=name, self.add_layout_pin_rect_center(text=name,
layer="m1", layer=self.route_layer,
offset=contact_offset, offset=contact_offset,
width=contact_m1_width, width=via.mod.second_layer_width,
height=contact_m1_height) height=via.mod.second_layer_height)
# This is to ensure that the contact is # This is to ensure that the contact is
# connected to the gate # connected to the gate
mid_point = contact_offset.scale(0.5, 1) \ 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, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="n", implant_type="n",
well_type="n") well_type="n",
if hasattr(self, "li_stack"): directions=("V", "V"))
self.add_via_center(layers=self.li_stack,
offset=contact_offset)
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)), offset=contact_offset + vector(0, 0.5 * (self.height - contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width, width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y) height=self.height - contact_offset.y)
@ -256,13 +260,10 @@ class pgate(design.design):
self.pwell_contact= self.add_via_center(layers=layer_stack, self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
implant_type="p", implant_type="p",
well_type="p") well_type="p",
directions=("V", "V"))
if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack, self.add_rect_center(layer=self.route_layer,
offset=contact_offset)
self.add_rect_center(layer="m1",
offset=contact_offset.scale(1, 0.5), offset=contact_offset.scale(1, 0.5),
width=self.pwell_contact.mod.second_layer_width, width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y) height=contact_offset.y)
@ -286,6 +287,18 @@ class pgate(design.design):
# offset=implant_offset, # offset=implant_offset,
# width=implant_width, # width=implant_width,
# height=implant_height) # 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): def determine_width(self):
""" Determine the width based on the well contacts (assumed to be on the right side) """ """ 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, min_channel = max(contact.poly_contact.width + self.m1_space,
contact.poly_contact.width + 2 * self.poly_to_active) 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 total_height = tx_height + min_channel + 2 * self.top_bottom_space
# debug.check(self.height > total_height, # debug.check(self.height > total_height,
# "Cell height {0} too small for simple min height {1}.".format(self.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, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer, add_drain_contact=self.route_layer,
connect_poly=True, connect_poly=True,
connect_drain_active=True) connect_drain_active=True)
@ -211,24 +204,12 @@ class pinv(pgate.pgate):
width=self.pmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer, add_drain_contact=self.route_layer,
connect_poly=True, connect_poly=True,
connect_drain_active=True) connect_drain_active=True)
self.add_mod(self.pmos) 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): def create_ptx(self):
""" """
Create the PMOS and NMOS netlist. Create the PMOS and NMOS netlist.

View File

@ -76,7 +76,7 @@ class pnand2(pgate.pgate):
width=self.nmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact="active") add_drain_contact="active")
self.add_mod(self.nmos_left) self.add_mod(self.nmos_left)
@ -92,7 +92,7 @@ class pnand2(pgate.pgate):
width=self.pmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer) add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left) self.add_mod(self.pmos_left)
@ -101,43 +101,17 @@ class pnand2(pgate.pgate):
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact=self.route_layer, add_source_contact=self.route_layer,
add_drain_contact="m1") add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right) self.add_mod(self.pmos_right)
def setup_layout_constants(self): def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """ """ 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, # Compute the other pmos2 location,
# but determining offset to overlap the # but determining offset to overlap the
# source and drain pins # source and drain pins
self.overlap_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center() 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): def create_ptx(self):
""" """
Add PMOS and NMOS to the netlist. Add PMOS and NMOS to the netlist.
@ -200,35 +174,46 @@ class pnand2(pgate.pgate):
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ 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 # 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.route_input_gate(self.pmos2_inst,
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, self.inputB_yoffset,
"B", "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.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
"A") "A",
position="center")
def route_output(self): def route_output(self):
""" Route the Z output """ """ 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 # PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D") pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center() top_pin_offset = pmos_pin.bc()
# NMOS2 drain # NMOS2 drain
nmos_pin = self.nmos2_inst.get_pin("D") nmos_pin = self.nmos2_inst.get_pin("D")
bottom_pin_offset = nmos_pin.center() bottom_pin_offset = nmos_pin.uc()
# Output pin # Output pin
out_offset = vector(nmos_pin.cx(), out_offset = vector(nmos_pin.cx() + self.route_layer_pitch,
self.inputA_yoffset) output_yoffset)
# This routes on M2 # This routes on M2
# # Midpoints of the L routes go horizontal first then vertical # # Midpoints of the L routes go horizontal first then vertical
@ -253,10 +238,15 @@ class pnand2(pgate.pgate):
# This routes on route_layer # This routes on route_layer
# Midpoints of the L routes goes vertical first then horizontal # 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, 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 # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",

View File

@ -5,7 +5,6 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import contact
import pgate import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
@ -14,6 +13,7 @@ import logical_effort
from sram_factory import factory from sram_factory import factory
from globals import OPTS from globals import OPTS
class pnand3(pgate.pgate): class pnand3(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nand. This module generates gds of a parametrically sized 2-input nand.
@ -94,7 +94,7 @@ class pnand3(pgate.pgate):
width=self.nmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact="active") add_drain_contact="active")
self.add_mod(self.nmos_left) self.add_mod(self.nmos_left)
@ -102,7 +102,7 @@ class pnand3(pgate.pgate):
width=self.pmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer) add_drain_contact=self.route_layer)
self.add_mod(self.pmos_left) self.add_mod(self.pmos_left)
@ -111,14 +111,14 @@ class pnand3(pgate.pgate):
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact=self.route_layer, add_source_contact=self.route_layer,
add_drain_contact="m1") add_drain_contact=self.route_layer)
self.add_mod(self.pmos_center) self.add_mod(self.pmos_center)
self.pmos_right = factory.create(module_type="ptx", self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer) add_drain_contact=self.route_layer)
self.add_mod(self.pmos_right) self.add_mod(self.pmos_right)
@ -132,21 +132,6 @@ class pnand3(pgate.pgate):
# to the active contacts # to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos") nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(), 0) 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): def create_ptx(self):
""" """
@ -223,37 +208,34 @@ class pnand3(pgate.pgate):
def route_inputs(self): def route_inputs(self):
""" Route the A and B and C inputs """ """ Route the A and B and C inputs """
m1_pitch = self.m1_space + contact.m1_via.first_layer_height pmos_drain_bottom = self.pmos1_inst.get_pin("D").by()
# Put B right on the well line self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
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")
# FIXME: constant hack self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
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.route_input_gate(self.pmos1_inst, self.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
"A", "A",
position="left") 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D") pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain # PMOS3 drain
@ -262,7 +244,7 @@ class pnand3(pgate.pgate):
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
out_offset = vector(nmos3_pin.cx() + self.route_layer_pitch, 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 # Go up to metal2 for ease on all output pins
# self.add_via_center(layers=self.m1_stack, # self.add_via_center(layers=self.m1_stack,

View File

@ -75,7 +75,7 @@ class pnor2(pgate.pgate):
width=self.nmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact=self.route_layer) add_drain_contact=self.route_layer)
self.add_mod(self.nmos_left) self.add_mod(self.nmos_left)
@ -84,14 +84,14 @@ class pnor2(pgate.pgate):
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_source_contact=self.route_layer, add_source_contact=self.route_layer,
add_drain_contact="m1") add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right) self.add_mod(self.nmos_right)
self.pmos_left = factory.create(module_type="ptx", self.pmos_left = factory.create(module_type="ptx",
width=self.pmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="pmos",
add_source_contact="m1", add_source_contact=self.route_layer,
add_drain_contact="active") add_drain_contact="active")
self.add_mod(self.pmos_left) self.add_mod(self.pmos_left)
@ -106,12 +106,6 @@ class pnor2(pgate.pgate):
def setup_layout_constants(self): def setup_layout_constants(self):
""" Pre-compute some handy layout parameters. """ """ 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 # Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins # 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() 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 + 0.5 * self.nwell_enclose_active
self.well_width = self.width + 2 * self.nwell_enclose_active self.well_width = self.width + 2 * self.nwell_enclose_active
# Height is an input parameter, so it is not recomputed. # 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): def create_ptx(self):
""" """
Add PMOS and NMOS to the layout at the upper-most and lowest position 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 Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel 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, pmos1_pos = vector(self.pmos_right.active_offset.x,
self.height - self.pmos_right.active_height \ self.height - self.pmos_right.active_height - self.top_bottom_space)
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos) self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset 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_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -205,22 +187,28 @@ class pnor2(pgate.pgate):
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ 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.route_input_gate(self.pmos2_inst,
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, self.inputB_yoffset,
"B", "B",
position="right") position="right",
directions=("V", "V"))
# This will help with the wells and the input/output placement # 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.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """

View File

@ -159,7 +159,7 @@ class precharge(design.design):
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout with 2 M2 tracks # 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_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos) 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 self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed. # 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): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx", self.nmos = factory.create(module_type="ptx",

View File

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

View File

@ -18,17 +18,13 @@ class router_tech:
""" """
def __init__(self, layers, rail_track_width): def __init__(self, layers, rail_track_width):
""" """
Allows us to change the layers that we are routing on. First layer Allows us to change the layers that we are routing on.
is always horizontal, middle is via, and last is always This uses the preferreed directions.
vertical.
""" """
self.layers = layers self.layers = layers
self.rail_track_width = rail_track_width self.rail_track_width = rail_track_width
if len(self.layers) == 1: 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_layer_name = self.vert_layer_name = self.layers[0]
self.horiz_lpp = self.vert_lpp = layer[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 # figure out wich of the two layers prefers horizontal/vertical
# routing # 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 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 self.vert_layer_name = try_vert_layer
else: else:
raise ValueError("Layer '{}' and '{}' are using the wrong " \ self.vert_layer_name = try_horiz_layer
"preferred_directions '{}' and '{}'. Only "\
"('H', 'V') are supported") 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)) via_connect = contact(self.layers, (1, 1))
max_via_size = max(via_connect.width,via_connect.height) 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) wmask_pos = [None] * len(self.all_ports)
data_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: if self.write_size:
max_gap_size = self.m3_pitch * self.word_size + 2 * self.m1_pitch self.data_bus_gap = self.m4_nonpref_pitch * 2
max_gap_size_wmask = self.m2_pitch * max(self.num_wmasks + 1, self.col_addr_size + 1) + 2 * self.m1_pitch 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: else:
# This is M2 pitch even though it is on M1 to help stem via spacings on the trunk self.data_bus_gap = self.m3_nonpref_pitch * 2
# The M1 pitch is for supply rail spacings self.data_bus_size = self.m3_nonpref_pitch * (max(self.word_size + 1, self.col_addr_size + 1)) + self.data_bus_gap
max_gap_size = self.m2_pitch * max(self.word_size + 1,self.col_addr_size + 1) + 2 * self.m1_pitch
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
port = 0 port = 0
@ -74,12 +83,12 @@ class sram_1bank(sram_base):
if self.write_size: if self.write_size:
# Add the write mask flops below the write mask AND array. # Add the write mask flops below the write mask AND array.
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 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]) self.wmask_dff_insts[port].place(wmask_pos[port])
# Add the data flops below the write mask flops. # Add the data flops below the write mask flops.
data_pos[port] = vector(self.bank.bank_array_ll.x, 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]) self.data_dff_insts[port].place(data_pos[port])
else: else:
# Add the data flops below the bank to the right of the lower-left of bank array # 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. # sense amps.
if port in self.write_ports: if port in self.write_ports:
data_pos[port] = vector(self.bank.bank_array_ll.x, 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]) self.data_dff_insts[port].place(data_pos[port])
else: else:
wmask_pos[port] = vector(self.bank.bank_array_ll.x, 0) 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.col_addr_dff:
if self.write_size: 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, 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: else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x - self.col_addr_dff_insts[port].width - self.bank.m2_gap, 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]) self.col_addr_dff_insts[port].place(col_addr_pos[port])
else: else:
col_addr_pos[port] = vector(self.bank.bank_array_ll.x, 0) 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: if self.write_size:
# Add the write mask flops below the write mask AND array. # 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, 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") self.wmask_dff_insts[port].place(wmask_pos[port], mirror="MX")
# Add the data flops below the write mask flops # 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, 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") self.data_dff_insts[port].place(data_pos[port], mirror="MX")
else: else:
# Add the data flops above the bank to the left of the upper-right of bank array # 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 # These flops go below the sensing and leave a gap to channel route to the
# sense amps. # sense amps.
data_pos[port] = vector(self.bank.bank_array_ur.x - self.data_dff_insts[port].width, 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") 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 # 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.col_addr_dff:
if self.write_size: if self.write_size:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, 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: else:
col_addr_pos[port] = vector(self.bank.bank_array_ur.x + self.bank.m2_gap, 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") self.col_addr_dff_insts[port].place(col_addr_pos[port], mirror="MX")
else: else:
col_addr_pos[port] = self.bank_inst.ur() 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 # 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. # to route vertically. For port1, it is to the left.
row_addr_clk_pin = self.row_addr_dff_insts[port].get_pin("clk") 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() control_clk_buf_pos = control_clk_buf_pin.lc()
row_addr_clk_pos = row_addr_clk_pin.lc() row_addr_clk_pos = row_addr_clk_pin.lc()
mid1_pos = vector(self.row_addr_dff_insts[port].lx() - self.m2_pitch, 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 # This is the steiner point where the net branches out
clk_steiner_pos = vector(mid1_pos.x, control_clk_buf_pos.y) 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_path(control_clk_buf_pin.layer, [control_clk_buf_pos, clk_steiner_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(from_layer=control_clk_buf_pin.layer,
offset=clk_steiner_pos) to_layer="m2",
offset=clk_steiner_pos)
# Note, the via to the control logic is taken care of above # 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]) [row_addr_clk_pos, mid1_pos, clk_steiner_pos])
if self.col_addr_dff: if self.col_addr_dff:
dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk") dff_clk_pin = self.col_addr_dff_insts[port].get_pin("clk")
dff_clk_pos = dff_clk_pin.center() dff_clk_pos = dff_clk_pin.center()
mid_pos = vector(clk_steiner_pos.x, dff_clk_pos.y) 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]) [dff_clk_pos, mid_pos, clk_steiner_pos])
if port in self.write_ports: if port in self.write_ports:
@ -282,7 +292,7 @@ class sram_1bank(sram_base):
self.add_path("m2", self.add_path("m2",
[mid_pos, clk_steiner_pos], [mid_pos, clk_steiner_pos],
width=max(m2_via.width, m2_via.height)) 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]) [data_dff_clk_pos, mid_pos, clk_steiner_pos])
if self.write_size: 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 # In some designs, the steiner via will be too close to the mid_pos via
# so make the wire as wide as the contacts # 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_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): def route_control_logic(self):
""" Route the control logic pins that are not inputs """ """ Route the control logic pins that are not inputs """
@ -304,14 +314,13 @@ class sram_1bank(sram_base):
continue continue
src_pin = self.control_logic_insts[port].get_pin(signal) src_pin = self.control_logic_insts[port].get_pin(signal)
dest_pin = self.bank_inst.get_pin(signal + "{}".format(port)) 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: for port in self.all_ports:
# Only input (besides pins) is the replica bitline # Only input (besides pins) is the replica bitline
src_pin = self.control_logic_insts[port].get_pin("rbl_bl") src_pin = self.control_logic_insts[port].get_pin("rbl_bl")
dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port)) dest_pin = self.bank_inst.get_pin("rbl_bl{}".format(port))
self.connect_hbus(src_pin, dest_pin)
self.connect_hbus_m2m3(src_pin, dest_pin)
def route_row_addr_dff(self): def route_row_addr_dff(self):
""" Connect the output of the row flops to the bank pins """ """ 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() flop_pos = flop_pin.center()
bank_pos = bank_pin.center() bank_pos = bank_pin.center()
mid_pos = vector(bank_pos.x, flop_pos.y) 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]) [flop_pos, mid_pos, bank_pos])
self.add_via_center(layers=self.m2_stack, self.add_via_stack_center(from_layer=flop_pin.layer,
offset=flop_pos) to_layer="m3",
offset=flop_pos)
def route_col_addr_dff(self): def route_col_addr_dff(self):
""" Connect the output of the col flops to the bank pins """ """ Connect the output of the col flops to the bank pins """
for port in self.all_ports: for port in self.all_ports:
if port%2: if port % 2:
offset = self.col_addr_dff_insts[port].ll() - vector(0, (self.col_addr_size + 2) * self.m1_pitch) offset = self.col_addr_dff_insts[port].ll() - vector(0, self.col_addr_bus_size)
else: 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)] bus_names = ["addr_{}".format(x) for x in range(self.col_addr_size)]
col_addr_bus_offsets = self.create_horizontal_bus(layer="m1", col_addr_bus_offsets = self.create_horizontal_bus(layer="m1",
pitch=self.m1_pitch,
offset=offset, offset=offset,
names=bus_names, names=bus_names,
length=self.col_addr_dff_insts[port].width) length=self.col_addr_dff_insts[port].width)
dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)] dff_names = ["dout_{}".format(x) for x in range(self.col_addr_size)]
data_dff_map = zip(dff_names, bus_names) 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)] bank_names = ["addr{0}_{1}".format(port, x) for x in range(self.col_addr_size)]
data_bank_map = zip(bank_names, bus_names) 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): def route_data_dff(self):
""" Connect the output of the data flops to the write driver """ """ 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: for port in self.write_ports:
if self.write_size: if self.write_size:
if port % 2: 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: 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: else:
if port % 2: 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: 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_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] dff_pins = [self.data_dff_insts[port].get_pin(x) for x in dff_names]
if self.write_size: if self.write_size:
for x in dff_names: 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, self.add_via_center(layers=self.m1_stack,
offset=pin_offset, offset=pin_offset,
directions=("V", "V")) directions=("V", "V"))
self.add_via_center(layers=self.m2_stack, self.add_via_stack_center(from_layer="m2",
offset=pin_offset) to_layer="m4",
self.add_via_center(layers=self.m3_stack, offset=pin_offset)
offset=pin_offset)
bank_names = ["din{0}_{1}".format(port, x) for x in range(self.word_size)] 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] bank_pins = [self.bank_inst.get_pin(x) for x in bank_names]
if self.write_size: if self.write_size:
for x in bank_names: for x in bank_names:
pin = self.bank_inst.get_pin(x)
if port % 2: if port % 2:
pin_offset = self.bank_inst.get_pin(x).uc() pin_offset = pin.uc()
else: else:
pin_offset = self.bank_inst.get_pin(x).bc() pin_offset = pin.bc()
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(from_layer=pin.layer,
offset=pin_offset) to_layer="m4",
self.add_via_center(layers=self.m2_stack, offset=pin_offset)
offset=pin_offset)
self.add_via_center(layers=self.m3_stack,
offset=pin_offset)
route_map = list(zip(bank_pins, dff_pins)) route_map = list(zip(bank_pins, dff_pins))
if self.write_size: if self.write_size:
layer_stack = self.m3_stack layer_stack = self.m3_stack
else: else:
layer_stack = self.m1_stack layer_stack = self.m1_stack
self.create_horizontal_channel_route(netlist=route_map, self.create_horizontal_channel_route(netlist=route_map,
offset=offset, offset=offset,
layer_stack=layer_stack) layer_stack=layer_stack)
@ -409,9 +421,9 @@ class sram_1bank(sram_base):
# This is where the channel will start (y-dimension at least) # This is where the channel will start (y-dimension at least)
for port in self.write_ports: for port in self.write_ports:
if port % 2: 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: 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_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] dff_pins = [self.wmask_dff_insts[port].get_pin(x) for x in dff_names]

View File

@ -7,6 +7,7 @@
# #
import datetime import datetime
import debug import debug
from math import log
from importlib import reload from importlib import reload
from vector import vector from vector import vector
from globals import OPTS, print_time 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, "vdd")
self.copy_power_pins(inst, "gnd") self.copy_power_pins(inst, "gnd")
import tech
if not OPTS.route_supplies: if not OPTS.route_supplies:
# Do not route the power supply (leave as must-connect pins) # Do not route the power supply (leave as must-connect pins)
return return
@ -148,6 +148,7 @@ class sram_base(design, verilog, lef):
grid_stack = power_grid grid_stack = power_grid
except ImportError: except ImportError:
# if no power_grid is specified by tech we use sensible defaults # if no power_grid is specified by tech we use sensible defaults
import tech
if "m4" in tech.layer: if "m4" in tech.layer:
# Route a M3/M4 grid # Route a M3/M4 grid
grid_stack = self.m3_stack grid_stack = self.m3_stack
@ -496,70 +497,6 @@ class sram_base(design, verilog, lef):
self.connect_inst(temp) self.connect_inst(temp)
return insts 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): def sp_write(self, sp_name, lvs_netlist=False):
# Write the entire spice of the object to the file # 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) tx = factory.create(module_type="pinv", size=1)
self.local_check(tx) self.local_check(tx)
globals.end_openram() globals.end_openram()
# run the test from the command line # run the test from the command line
if __name__ == "__main__": 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")) config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file) 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") debug.info(1, "Testing 16 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=16) a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a) 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") debug.info(1, "Testing 17 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=17) a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 23 row sample for hierarchical_decoder") # Checks 2x4 with 3x8 and 2-input NAND decoder
a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder") debug.info(1, "Testing 32 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=32) a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 65 row sample for hierarchical_decoder") # Checks 3 x 2x4 and 3-input NAND decoder
a = factory.create(module_type="hierarchical_decoder", num_outputs=65) debug.info(1, "Testing 64 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=64)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder") # Checks 2x4 and 2 x 3x8 and 3-input NAND with non-power-of-two
a = factory.create(module_type="hierarchical_decoder", num_outputs=128) debug.info(1, "Testing 132 row sample for hierarchical_decoder")
self.local_check(a) a = factory.create(module_type="hierarchical_decoder", num_outputs=132)
debug.info(1, "Testing 341 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a) self.local_check(a)
# Checks 3 x 3x8 and 3-input NAND decoder
debug.info(1, "Testing 512 row sample for hierarchical_decoder") debug.info(1, "Testing 512 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", num_outputs=512) a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a) self.local_check(a)

View File

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

View File

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

View File

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

View File

@ -32,7 +32,7 @@ tech_modules = module_type()
cell_properties = cell_properties() cell_properties = cell_properties()
cell_properties.bitcell.mirror.x = True cell_properties.bitcell.mirror.x = True
cell_properties.bitcell.mirror.y = False cell_properties.bitcell.mirror.y = False
cell_properties.bitcell_power_pin_directions = ("V", "V")
################################################### ###################################################
# GDS file info # GDS file info
@ -63,6 +63,13 @@ m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3") m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4") 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 # The FEOL stacks get us up to m1
feol_stacks = [poly_stack, feol_stacks = [poly_stack,
active_stack] active_stack]

View File

@ -60,6 +60,13 @@ m1_stack = ("m1", "via1", "m2")
m2_stack = ("m2", "via2", "m3") m2_stack = ("m2", "via2", "m3")
m3_stack = ("m3", "via3", "m4") 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 # The FEOL stacks get us up to m1
feol_stacks = [poly_stack, feol_stacks = [poly_stack,
active_stack] active_stack]