Merge branch 'dev' into bisr

This commit is contained in:
Aditi Sinha 2020-05-13 22:39:29 +00:00
commit a5c211bd90
53 changed files with 1540 additions and 1306 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,47 +32,97 @@ 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:
contact_width = contact1.first_layer_width
layer_space = getattr(self, layer1 + "_space")
layer1_space = getattr(self, layer1 + "_space") #print(layer_stack)
layer2_space = getattr(self, layer2 + "_space") #print(contact1)
pitch = max_contact + max(layer1_space, layer2_space) pitch = contact_width + layer_space
return pitch return round_to_grid(pitch)
def setup_drc_constants(self): def setup_drc_constants(self):
""" """

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
@ -41,12 +42,15 @@ class layout():
self.visited = [] # List of modules we have already visited self.visited = [] # List of modules we have already visited
self.is_library_cell = False # Flag for library cells self.is_library_cell = False # Flag for library cells
self.gds_read() self.gds_read()
try: try:
from tech import power_grid from tech import power_grid
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"
############################################################ ############################################################
# GDS layout # GDS layout
############################################################ ############################################################
@ -444,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
@ -452,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 """
@ -461,11 +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. """
if not directions:
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,
@ -486,11 +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.
""" """
if not directions:
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,
@ -513,36 +541,47 @@ class layout():
return inst return inst
def add_via_stack(self, offset, from_layer, to_layer, def add_via_stack(self, offset, from_layer, to_layer,
direction=None, directions=None,
size=[1, 1]): size=[1, 1],
implant_type=None,
well_type=None):
""" """
Punch a stack of vias from a start layer to a target layer. Punch a stack of vias from a start layer to a target layer.
""" """
return self.__add_via_stack_internal(offset=offset, return self.__add_via_stack_internal(offset=offset,
direction=direction, directions=directions,
from_layer=from_layer, from_layer=from_layer,
to_layer=to_layer, to_layer=to_layer,
via_func=self.add_via, via_func=self.add_via,
last_via=None, last_via=None,
size=size) size=size,
implant_type=implant_type,
well_type=well_type)
def add_via_stack_center(self, offset, from_layer, to_layer, def add_via_stack_center(self,
direction=None, offset,
size=[1, 1]): from_layer,
to_layer,
directions=None,
size=[1, 1],
implant_type=None,
well_type=None):
""" """
Punch a stack of vias from a start layer to a target layer by the center Punch a stack of vias from a start layer to a target layer by the center
coordinate accounting for mirroring and rotation. coordinate accounting for mirroring and rotation.
""" """
return self.__add_via_stack_internal(offset=offset, return self.__add_via_stack_internal(offset=offset,
direction=direction, directions=directions,
from_layer=from_layer, from_layer=from_layer,
to_layer=to_layer, to_layer=to_layer,
via_func=self.add_via_center, via_func=self.add_via_center,
last_via=None, last_via=None,
size=size) size=size,
implant_type=implant_type,
well_type=well_type)
def __add_via_stack_internal(self, offset, direction, from_layer, to_layer, def __add_via_stack_internal(self, offset, directions, from_layer, to_layer,
via_func, last_via, size): via_func, last_via, size, implant_type=None, well_type=None):
""" """
Punch a stack of vias from a start layer to a target layer. Here we Punch a stack of vias from a start layer to a target layer. Here we
figure out whether to punch it up or down the stack. figure out whether to punch it up or down the stack.
@ -551,8 +590,8 @@ class layout():
if from_layer == to_layer: if from_layer == to_layer:
return last_via return last_via
from_id = int(from_layer[1]) from_id = layer_indices[from_layer]
to_id = int(to_layer[1]) 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
@ -563,18 +602,24 @@ class layout():
curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None) curr_stack = next(filter(lambda stack: stack[search_id] == from_layer, layer_stacks), None)
if curr_stack is None: if curr_stack is None:
raise ValueError("Cannot create via from '{0}' to '{1}'." \ raise ValueError("Cannot create via from '{0}' to '{1}'."
"Layer '{0}' not defined" "Layer '{0}' not defined".format(from_layer, to_layer))
.format(from_layer, to_layer))
via = via_func(layers=curr_stack, size=size, offset=offset, directions=direction) via = via_func(layers=curr_stack,
return self.__add_via_stack_internal(offset=offset, size=size,
direction=direction, offset=offset,
from_layer=curr_stack[next_id], directions=directions,
to_layer=to_layer, implant_type=implant_type,
via_func=via_func, well_type=well_type)
last_via=via,
size=size) via = self.__add_via_stack_internal(offset=offset,
directions=directions,
from_layer=curr_stack[next_id],
to_layer=to_layer,
via_func=via_func,
last_via=via,
size=size)
return via
def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"): def add_ptx(self, offset, mirror="R0", rotate=0, width=1, mults=1, tx_type="nmos"):
"""Adds a ptx module to the design.""" """Adds a ptx module to the design."""
@ -729,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.
@ -779,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,
@ -848,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
@ -866,32 +916,75 @@ 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,
@ -906,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!
@ -975,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):
@ -999,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):
@ -1017,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
@ -1028,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]
@ -1039,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:
@ -1048,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
@ -1070,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:
@ -1081,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():
@ -1119,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 """
@ -1200,27 +1299,18 @@ class layout():
"supply router." "supply router."
.format(name, inst.name, self.pwr_grid_layer)) .format(name, inst.name, self.pwr_grid_layer))
def add_power_pin(self, name, loc, size=[1, 1], vertical=False, start_layer="m1"): def add_power_pin(self, name, loc, size=[1, 1], directions=None, start_layer="m1"):
""" """
Add a single power pin from the lowest power_grid layer down to M1 (or li) at Add a single power pin from the lowest power_grid layer down to M1 (or li) at
the given center location. The starting layer is specified to determine the given center location. The starting layer is specified to determine
which vias are needed. which vias are needed.
""" """
# Force vdd/gnd via stack to be vertically or horizontally oriented
# Default: None, uses prefered metal directions
if vertical:
direction = ("V", "V")
elif not vertical and vertical is not None:
direction = ("H", "H")
else:
direction = None
via = self.add_via_stack_center(from_layer=start_layer, via = self.add_via_stack_center(from_layer=start_layer,
to_layer=self.pwr_grid_layer, to_layer=self.pwr_grid_layer,
size=size, size=size,
offset=loc, offset=loc,
direction=direction) directions=directions)
if start_layer == self.pwr_grid_layer: if start_layer == self.pwr_grid_layer:
self.add_layout_pin_rect_center(text=name, self.add_layout_pin_rect_center(text=name,
layer=self.pwr_grid_layer, layer=self.pwr_grid_layer,

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

@ -25,6 +25,7 @@ def create_rectilinear_route(my_list):
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
@ -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."""

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

@ -86,10 +86,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 + self.num_spare_cols): for bit in range(self.word_size + self.num_spare_cols):
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_*.
@ -133,16 +133,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,
@ -643,11 +641,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,
@ -658,11 +655,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,
@ -862,9 +858,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]
@ -872,7 +868,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):
""" """
@ -928,30 +926,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

@ -104,9 +104,9 @@ class bitcell_base_array(design.design):
# For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space # For non-square via stacks, vertical/horizontal direction refers to the stack orientation in 2d space
# Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20) # Default uses prefered directions for each layer; this cell property is only currently used by s8 tech (03/20)
try: try:
force_power_pins_vertical = cell_properties.bitcell_force_power_pins_vertical bitcell_power_pin_directions = cell_properties.bitcell_power_pin_directions
except AttributeError: except AttributeError:
force_power_pins_vertical = None bitcell_power_pin_directions = None
# Add vdd/gnd via stacks # Add vdd/gnd via stacks
for row in range(self.row_size): for row in range(self.row_size):
@ -114,7 +114,10 @@ class bitcell_base_array(design.design):
inst = self.cell_inst[row,col] inst = self.cell_inst[row,col]
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
for pin in inst.get_pins(pin_name): for pin in inst.get_pins(pin_name):
self.add_power_pin(name=pin_name, loc=pin.center(), vertical=force_power_pins_vertical, start_layer=pin.layer) self.add_power_pin(name=pin_name,
loc=pin.center(),
directions=bitcell_power_pin_directions,
start_layer=pin.layer)
def _adjust_x_offset(self, xoffset, col, col_offset): def _adjust_x_offset(self, xoffset, col, col_offset):
tempx = xoffset tempx = xoffset

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 """
@ -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
@ -556,29 +551,24 @@ class control_logic(design.design):
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,10 +641,15 @@ 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")
@ -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,
@ -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

@ -62,7 +62,7 @@ class delay_chain(design.design):
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
def add_modules(self): def add_modules(self):
self.inv = factory.create(module_type="pinv", route_output=False) self.inv = factory.create(module_type="pinv")
self.add_mod(self.inv) self.add_mod(self.inv)
def create_inverters(self): def create_inverters(self):

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,19 +187,36 @@ 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):
nand_width = self.and2.width nand_width = self.and2.width
nand_inputs = 2
else: else:
nand_width = self.and3.width nand_width = self.and3.width
self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1) nand_inputs = 3
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.m2_pitch
# print(self.decoders_per_row, nand_inputs)
# print(decoder_input_wire_height, self.cell_height)
if decoder_input_wire_height > self.cell_height:
debug.warning("Cannot fit multi-bit decoder routes per row.")
# debug.check(decoder_input_wire_height < self.cell_height, "Cannot fit multi-bit decoder routes per row.")
self.input_routing_width = (self.num_inputs + 1) * self.m2_pitch self.input_routing_width = (self.num_inputs + 1) * self.m2_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
@ -218,7 +236,6 @@ class hierarchical_decoder(design.design):
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="m2", self.input_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch,
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=input_height) length=input_height)
@ -238,7 +255,7 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so # To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails # that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
@ -254,7 +271,7 @@ class hierarchical_decoder(design.design):
# To prevent conflicts, we will offset each input connect so # To prevent conflicts, we will offset each input connect so
# that it aligns with the vdd/gnd rails # that it aligns with the vdd/gnd rails
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * (self.inv.height + self.m1_pitch))
input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1) input_offset = input_pos.scale(1, 0) + decoder_offset.scale(0, 1)
self.route_input_bus(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
@ -265,11 +282,13 @@ class hierarchical_decoder(design.design):
vertical M2 coordinate to the predecode inputs vertical M2 coordinate to the predecode inputs
""" """
self.add_via_center(layers=self.m2_stack, self.add_via_stack_center(from_layer="m2",
offset=input_offset) to_layer="m3",
self.add_via_center(layers=self.m2_stack, offset=input_offset)
offset=output_offset) self.add_via_stack_center(from_layer="m2",
self.add_path(("m3"), [input_offset, output_offset]) to_layer="m3",
offset=output_offset)
self.add_path("m3", [input_offset, output_offset])
def add_pins(self): def add_pins(self):
""" Add the module pins """ """ Add the module pins """
@ -411,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):
""" """
@ -447,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):
""" """
@ -468,7 +499,7 @@ class hierarchical_decoder(design.design):
# 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="m2", self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_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)
@ -512,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):
@ -536,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.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)
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.m3_pitch) output_index,
2)
output_index = output_index + 1 output_index = output_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -570,38 +606,55 @@ 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
""" """
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() pin_pos = pin.center()
# If we have a single decoder per row, we can route on M1
if self.decoders_per_row == 1: # y_offset is the same for both the M2 and M4 routes so that the rail
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y) # contacts align and don't cause problems
self.add_path("m1", [rail_pos, pin_pos]) if pin_index == 0:
self.add_via_center(layers=self.m1_stack, # Bottom pitch
offset=rail_pos) y_offset = row_offset
# If not, we must route over the decoder cells on M3 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: else:
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset) debug.error("Invalid decoder pitch.")
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos]) rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
self.add_via_center(layers=self.m2_stack, mid_pos = vector(pin_pos.x, rail_pos.y)
offset=rail_pos) self.add_wire(self.decoder_layers[row_remainder], [rail_pos, mid_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=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):
""" """
@ -610,13 +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_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos]) to_layer="m1",
self.add_via_center(layers=self.m1_stack, offset=pin_pos)
offset=rail_pos) self.add_via_stack_center(from_layer="m1",
to_layer="m2",
offset=rail_pos)
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.m2_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.m2_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.m2_width, self.m1_pitch) offset = vector(0.5 * self.m2_width, self.m3_pitch)
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_rails = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_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.m2_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="m2", self.decode_rails = self.create_vertical_bus(layer="m2",
pitch=self.m2_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)
@ -146,16 +155,18 @@ class hierarchical_predecode(design.design):
# route one signal next to each vdd/gnd rail since this is # route one signal next to each vdd/gnd rail since this is
# typically where the p/n devices are and there are no # typically where the p/n devices are and there are no
# pins in the and gates. # pins in the and gates.
y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m1_via.width + self.m1_space y_offset = (num + self.number_of_inputs) * self.inv.height + contact.m2_via.width + self.m2_space
in_pin = "in_{}".format(num) in_pin = "in_{}".format(num)
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("m1", [in_pos, a_pos]) self.add_path("m1", [in_pos, a_pos])
self.add_via_center(layers=self.m1_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.m1_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):
""" """
@ -165,7 +176,7 @@ class hierarchical_predecode(design.design):
z_pin = self.and_inst[num].get_pin("Z") z_pin = self.and_inst[num].get_pin("Z")
self.add_layout_pin(text="out_{}".format(num), self.add_layout_pin(text="out_{}".format(num),
layer="m1", layer=z_pin.layer,
offset=z_pin.ll(), offset=z_pin.ll(),
height=z_pin.height(), height=z_pin.height(),
width=z_pin.width()) width=z_pin.width())
@ -178,24 +189,30 @@ class hierarchical_predecode(design.design):
out_pin = "Abar_{}".format(inv_num) out_pin = "Abar_{}".format(inv_num)
in_pin = "in_{}".format(inv_num) in_pin = "in_{}".format(inv_num)
#add output so that it is just below the vdd or gnd rail # add output so that it is just below the vdd or gnd rail
# since this is where the p/n devices are and there are no # since this is where the p/n devices are and there are no
# pins in the and gates. # pins in the and gates.
y_offset = (inv_num+1) * self.inv.height - 3*self.m1_space y_offset = (inv_num + 1) * self.inv.height - 3 * self.m1_space
inv_out_pos = self.in_inst[inv_num].get_pin("Z").rc() inv_out_pin = self.in_inst[inv_num].get_pin("Z")
right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(),0) inv_out_pos = inv_out_pin.rc()
rail_pos = vector(self.decode_rails[out_pin].x,y_offset) right_pos = inv_out_pos + vector(self.inv.width - self.inv.get_pin("Z").lx(), 0)
self.add_path("m1", [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos]) rail_pos = vector(self.decode_rails[out_pin].x, y_offset)
self.add_via_center(layers = self.m1_stack, self.add_path(inv_out_pin.layer, [inv_out_pos, right_pos, vector(right_pos.x, y_offset), rail_pos])
offset=rail_pos) self.add_via_stack_center(from_layer=inv_out_pin.layer,
to_layer="m2",
offset=rail_pos)
# route input
#route input pin = self.in_inst[inv_num].get_pin("A")
inv_in_pos = self.in_inst[inv_num].get_pin("A").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("m1", [in_pos, inv_in_pos]) self.add_path("m1", [in_pos, inv_in_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(from_layer=pin.layer,
offset=in_pos) to_layer="m1",
offset=inv_in_pos)
self.add_via_stack_center(from_layer="m1",
to_layer="m2",
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
@ -205,17 +222,28 @@ class hierarchical_predecode(design.design):
index_lst= and_input_line_combination[k] index_lst= and_input_line_combination[k]
if self.number_of_inputs == 2: if self.number_of_inputs == 2:
gate_lst = ["A","B"] gate_lst = ["A", "B"]
else: else:
gate_lst = ["A","B","C"] gate_lst = ["A", "B", "C"]
# this will connect pins A,B or A,B,C # this will connect pins A,B or A,B,C
for rail_pin,gate_pin in zip(index_lst,gate_lst): for rail_pin, gate_pin in zip(index_lst, gate_lst):
pin_pos = self.and_inst[k].get_pin(gate_pin).lc() pin = self.and_inst[k].get_pin(gate_pin)
pin_pos = pin.center()
rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y) rail_pos = vector(self.decode_rails[rail_pin].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos]) self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_stack_center(from_layer="m1",
offset=rail_pos) to_layer="m2",
offset=rail_pos)
if gate_pin == "A":
direction = None
else:
direction = ("H", "H")
self.add_via_stack_center(from_layer=pin.layer,
to_layer="m1",
offset=pin_pos,
directions=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. """
@ -230,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, ceil from math import log, ceil
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 = ceil(log(self.num_rows, 2)) self.addr_size = ceil(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("gnd","GROUND")
self.add_pin("vdd", "POWER")
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,8 +116,6 @@ 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 """
@ -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

@ -789,7 +789,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

@ -355,31 +355,29 @@ 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 """

View File

@ -151,13 +151,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
@ -26,8 +26,8 @@ class wordline_driver(design.design):
debug.info(1, "Creating {0}".format(self.name)) debug.info(1, "Creating {0}".format(self.name))
self.add_comment("rows: {0} cols: {1}".format(rows, cols)) self.add_comment("rows: {0} cols: {1}".format(rows, cols))
self.rows = rows self.bitcell_rows = rows
self.cols = cols self.bitcell_cols = cols
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")
try: try:
@ -36,6 +36,11 @@ class wordline_driver(design.design):
self.cell_multiple = 1 self.cell_multiple = 1
self.cell_height = self.cell_multiple * b.height self.cell_height = self.cell_multiple * b.height
# We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.bitcell_rows / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.bitcell_rows / self.num_rows)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
@ -56,10 +61,10 @@ class wordline_driver(design.design):
def add_pins(self): def add_pins(self):
# inputs to wordline_driver. # inputs to wordline_driver.
for i in range(self.rows): for i in range(self.bitcell_rows):
self.add_pin("in_{0}".format(i), "INPUT") self.add_pin("in_{0}".format(i), "INPUT")
# Outputs from wordline_driver. # Outputs from wordline_driver.
for i in range(self.rows): for i in range(self.bitcell_rows):
self.add_pin("wl_{0}".format(i), "OUTPUT") self.add_pin("wl_{0}".format(i), "OUTPUT")
self.add_pin("en", "INPUT") self.add_pin("en", "INPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
@ -68,7 +73,7 @@ class wordline_driver(design.design):
def add_modules(self): def add_modules(self):
self.and2 = factory.create(module_type="pand2", self.and2 = factory.create(module_type="pand2",
height=self.cell_height, height=self.cell_height,
size=self.cols) size=self.bitcell_cols)
self.add_mod(self.and2) self.add_mod(self.and2)
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -79,7 +84,7 @@ class wordline_driver(design.design):
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
xoffset_list = [self.and_inst[0].lx()] xoffset_list = [self.and_inst[0].lx()]
for num in range(self.rows): for num in range(self.bitcell_rows):
# this will result in duplicate polygons for rails, but who cares # this will result in duplicate polygons for rails, but who cares
# use the inverter offset even though it will be the and's too # use the inverter offset even though it will be the and's too
@ -97,32 +102,34 @@ class wordline_driver(design.design):
def create_drivers(self): def create_drivers(self):
self.and_inst = [] self.and_inst = []
for row in range(self.rows): for row in range(self.bitcell_rows):
name_and = "wl_driver_and{}".format(row) name_and = "wl_driver_and{}".format(row)
# add and2 # add and2
self.and_inst.append(self.add_inst(name=name_and, self.and_inst.append(self.add_inst(name=name_and,
mod=self.and2)) mod=self.and2))
self.connect_inst(["en", self.connect_inst(["in_{0}".format(row),
"in_{0}".format(row), "en",
"wl_{0}".format(row), "wl_{0}".format(row),
"vdd", "gnd"]) "vdd", "gnd"])
def setup_layout_constants(self): def setup_layout_constants(self):
# We may have more than one bitcell per decoder row # We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.rows / self.cell_multiple) self.driver_rows = math.ceil(self.bitcell_rows / 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.rows / self.num_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
self.width = and2_xoffset + self.and2.width # Leave a well gap to separate the bitcell array well from this well
self.height = self.and2.height * self.num_rows well_gap = 2 * drc("pwell_to_nwell") + drc("nwell_enclose_active")
for row in range(self.rows): self.width = self.decoders_per_row * self.and2.width + well_gap
#row = math.floor(inst_index / self.decoders_per_row) self.height = self.and2.height * self.driver_rows
#dec = inst_index % self.decoders_per_row
for inst_index in range(self.bitcell_rows):
row = math.floor(inst_index / self.decoders_per_row)
dec = inst_index % self.decoders_per_row
if (row % 2): if (row % 2):
y_offset = self.and2.height * (row + 1) y_offset = self.and2.height * (row + 1)
@ -131,63 +138,42 @@ class wordline_driver(design.design):
y_offset = self.and2.height * row y_offset = self.and2.height * row
inst_mirror = "R0" inst_mirror = "R0"
# x_off = self.internal_routing_width + dec * and_mod.width x_offset = dec * self.and2.width
and2_offset = [and2_xoffset, y_offset] and2_offset = [x_offset, y_offset]
# add and2 # add and2
self.and_inst[row].place(offset=and2_offset, self.and_inst[inst_index].place(offset=and2_offset,
mirror=inst_mirror) mirror=inst_mirror)
def route_layout(self): def route_layout(self):
""" 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 row in range(self.rows): for inst_index in range(self.bitcell_rows):
and_inst = self.and_inst[row] and_inst = self.and_inst[inst_index]
row = math.floor(inst_index / self.decoders_per_row)
# en connection # Drop a via
a_pin = and_inst.get_pin("A")
a_pos = a_pin.lc()
clk_offset = vector(en_pin.bc().x, a_pos.y)
self.add_segment_center(layer="m1",
start=clk_offset,
end=a_pos)
self.add_via_center(layers=self.m1_stack,
offset=clk_offset)
# connect the decoder input pin to and2 B
b_pin = and_inst.get_pin("B") b_pin = and_inst.get_pin("B")
b_pos = b_pin.lc() self.add_via_stack_center(from_layer=b_pin.layer,
# needs to move down since B and input is to_layer="m2",
# nearly aligned with A inv input offset=b_pin.center())
up_or_down = self.m2_space if row % 2 else -self.m2_space
input_offset = vector(0, b_pos.y + up_or_down)
base_offset = vector(clk_offset.x, input_offset.y)
contact_offset = vector(0.5 * self.m2_width + self.m2_space + 0.5 * contact.m1_via.width, 0)
mid_via_offset = base_offset + contact_offset
# connect the decoder input pin to and2 A
a_pin = and_inst.get_pin("A")
a_pos = a_pin.center()
# 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",
start=input_offset, start=vector(0, a_pos.y),
end=mid_via_offset) end=a_pos)
self.add_via_center(layers=self.m1_stack,
offset=mid_via_offset,
directions=("V", "V"))
# now connect to the and2 B
self.add_path("m2", [mid_via_offset, b_pos])
contact_offset = b_pos - vector(0.5 * contact.m1_via.height, 0)
self.add_via_center(layers=self.m1_stack,
offset=contact_offset,
directions=("H", "H"))
# output each WL on the right # output each WL on the right
wl_offset = and_inst.get_pin("Z").rc() wl_offset = and_inst.get_pin("Z").rc()

View File

@ -209,7 +209,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()
@ -76,26 +77,10 @@ class pand2(pgate.pgate):
a2_pin = self.inv_inst.get_pin("A") a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy()) mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("m1", self.add_path(self.route_layer,
[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()
@ -77,26 +78,10 @@ class pand3(pgate.pgate):
a2_pin = self.inv_inst.get_pin("A") a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy()) mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy()) mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("m1", self.add_path(z1_pin.layer,
[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):
@ -141,26 +142,11 @@ class pdriver(pgate.pgate):
z_inst_list.append(self.inv_inst_list[x].get_pin("Z")) z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A")) a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy()) mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
self.add_path("m1", self.add_path(self.route_layer,
[z_inst_list[x].center(), mid_point, [z_inst_list[x].center(), mid_point,
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,24 @@ 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:
self.route_layer = "li"
else:
self.route_layer = "m1"
self.route_layer_width = getattr(self, "{}_width".format(self.route_layer))
self.route_layer_space = getattr(self, "{}_space".format(self.route_layer))
self.route_layer_pitch = getattr(self, "{}_pitch".format(self.route_layer))
# This is the space from a S/D contact to the supply rail
# Assume the contact starts at the active edge
contact_to_vdd_rail_space = 0.5 * self.m1_width + self.m1_space
# This is a poly-to-poly of a flipped cell
poly_to_poly_gate_space = self.poly_extend_active + self.poly_space
self.top_bottom_space = max(contact_to_vdd_rail_space,
poly_to_poly_gate_space)
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
@ -47,27 +64,27 @@ class pgate(design.design):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_layout.", -1) debug.error("Must over-ride create_layout.", -1)
def connect_pin_to_rail(self, inst, pin, supply): def connect_pin_to_rail(self, inst, pin_name, supply_name):
""" Connects a ptx pin to a supply rail. """ """ Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin) supply_pin = self.get_pin(supply_name)
supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin):
return
if supply == "gnd": source_pins = inst.get_pins(pin_name)
height = supply_pin.by() - source_pin.by() for source_pin in source_pins:
elif supply == "vdd":
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.", -1)
if abs(height) > 0: if supply_name == "gnd":
self.add_rect(layer="m1", height = supply_pin.by() - source_pin.by()
elif supply_name == "vdd":
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.", -1)
debug.check(supply_pin.layer == source_pin.layer, "Supply pin is not on correct layer.")
self.add_rect(layer=source_pin.layer,
offset=source_pin.ll(), 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
@ -93,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 \
@ -111,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) \
@ -194,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)
@ -249,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_rect_center(layer=self.route_layer,
self.add_via_center(layers=self.li_stack,
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)
@ -280,6 +288,18 @@ class pgate(design.design):
# 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) """
# Width is determined by well contact and spacing and allowing a supply via between each cell # Width is determined by well contact and spacing and allowing a supply via between each cell
@ -287,7 +307,8 @@ class pgate(design.design):
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.
def bin_width(self, tx_type, target_width): @staticmethod
def bin_width(tx_type, target_width):
if tx_type == "nmos": if tx_type == "nmos":
bins = nmos_bins[drc("minwidth_poly")] bins = nmos_bins[drc("minwidth_poly")]
@ -303,22 +324,24 @@ class pgate(design.design):
scaled_bin = bins[0] * scaling_factor scaled_bin = bins[0] * scaling_factor
else: else:
base_bins = []
scaled_bins = [] scaled_bins = []
scaling_factors = [] scaling_factors = []
scaled_bins.append(bins[-1]) scaled_bins.append(bins[-1])
base_bins.append(bins[-1])
scaling_factors.append(1) scaling_factors.append(1)
for width in bins[0:-1]: for width in bins[0:-1]:
m = math.ceil(target_width / width) m = math.ceil(target_width / width)
base_bins.append(width)
scaling_factors.append(m) scaling_factors.append(m)
scaled_bins.append(m * width) scaled_bins.append(m * width)
select = bisect_left(scaled_bins, target_width) select = bisect_left(scaled_bins, target_width)
scaling_factor = scaling_factors[select] scaling_factor = scaling_factors[select]
scaled_bin = scaled_bins[select] scaled_bin = scaled_bins[select]
select = (select + 1) % len(scaled_bins) selected_bin = base_bins[select]
selected_bin = bins[select]
debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, scaled_bin, target_width)) debug.info(2, "binning {0} tx, target: {4}, found {1} x {2} = {3}".format(tx_type, selected_bin, scaling_factor, selected_bin * scaling_factor, target_width))
return(selected_bin, scaling_factor) return(selected_bin, scaling_factor)

View File

@ -22,17 +22,17 @@ from errors import drc_error
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 pinv(pgate.pgate): class pinv(pgate.pgate):
""" """
Pinv generates gds of a parametrically sized inverter. The Pinv generates gds of a parametrically sized inverter. The
size is specified as the drive size (relative to minimum NMOS) and size is specified as the drive size (relative to minimum NMOS) and
a beta value for choosing the pmos size. The inverter's cell a beta value for choosing the pmos size. The inverter's cell
height is usually the same as the 6t library cell and is measured height is usually the same as the 6t library cell and is measured
from center of rail to rail.. The route_output will route the from center of rail to rail.
output to the right side of the cell for easier access.
""" """
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): def __init__(self, name, size=1, beta=parameter["beta"], height=None):
debug.info(2, debug.info(2,
"creating pinv structure {0} with size of {1}".format(name, "creating pinv structure {0} with size of {1}".format(name,
@ -43,7 +43,6 @@ class pinv(pgate.pgate):
self.nmos_size = size self.nmos_size = size
self.pmos_size = beta * size self.pmos_size = beta * size
self.beta = beta self.beta = beta
self.route_output = False
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
@ -108,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,
@ -202,30 +194,22 @@ 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=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True, connect_poly=True,
connect_active=True) connect_drain_active=True)
self.add_mod(self.nmos) self.add_mod(self.nmos)
self.pmos = factory.create(module_type="ptx", self.pmos = 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=self.route_layer,
add_drain_contact=self.route_layer,
connect_poly=True, connect_poly=True,
connect_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.
@ -274,24 +258,16 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS # Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc() nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("m1", [nmos_drain_pos, pmos_drain_pos]) self.add_path(self.route_layer, [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output # Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y) mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
if self.route_output: # This leaves the output as an internal pin (min sized)
# This extends the output to the edge of the cell output_offset = mid_drain_offset + vector(0.5 * self.route_layer_width, 0)
output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0) self.add_layout_pin_rect_center(text="Z",
self.add_layout_pin_segment_center(text="Z", layer=self.route_layer,
layer="m1", offset=output_offset)
start=mid_drain_offset,
end=output_offset)
else:
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="m1",
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))
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 """

View File

@ -72,65 +72,45 @@ class pnand2(pgate.pgate):
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos_nd = factory.create(module_type="ptx", self.nmos_left = factory.create(module_type="ptx",
width=self.nmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
add_drain_contact=False, add_source_contact=self.route_layer,
connect_poly=True, add_drain_contact="active")
connect_active=True) self.add_mod(self.nmos_left)
self.add_mod(self.nmos_nd)
self.nmos_ns = factory.create(module_type="ptx", self.nmos_right = factory.create(module_type="ptx",
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=False, add_source_contact="active",
connect_poly=True, add_drain_contact=self.route_layer)
connect_active=True) self.add_mod(self.nmos_right)
self.add_mod(self.nmos_ns)
self.pmos = 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",
connect_poly=True, add_source_contact=self.route_layer,
connect_active=True) add_drain_contact=self.route_layer)
self.add_mod(self.pmos) self.add_mod(self.pmos_left)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact=self.route_layer,
add_drain_contact=self.route_layer)
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.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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_nd.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):
""" """
@ -138,19 +118,19 @@ class pnand2(pgate.pgate):
""" """
self.pmos1_inst = self.add_inst(name="pnand2_pmos1", self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos) mod=self.pmos_left)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2", self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos) mod=self.pmos_right)
self.connect_inst(["Z", "B", "vdd", "vdd"]) self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand2_nmos1", self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos_nd) mod=self.nmos_left)
self.connect_inst(["Z", "B", "net1", "gnd"]) self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand2_nmos2", self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos_ns) mod=self.nmos_right)
self.connect_inst(["net1", "A", "gnd", "gnd"]) self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
@ -159,35 +139,29 @@ class pnand2(pgate.pgate):
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, pmos1_pos = vector(self.pmos_left.active_offset.x,
self.height - self.pmos.active_height \ self.height - self.pmos_left.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
self.pmos2_inst.place(self.pmos2_pos) self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x, nmos1_pos = vector(self.pmos_left.active_offset.x,
self.top_bottom_space) self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) self.nmos1_inst.place(nmos1_pos)
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)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height))
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
AFTER the wells are created AFTER the wells are created
""" """
self.add_nwell_contact(self.pmos, self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
self.pmos2_pos + vector(self.m1_pitch, 0)) self.add_pwell_contact(self.nmos_left, self.nmos2_pos)
self.add_pwell_contact(self.nmos_nd,
self.nmos2_pos + vector(self.m1_pitch, 0))
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
@ -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 """
inputB_yoffset = self.nmos2_inst.uy() + 0.5 * contact.poly_contact.height
# 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
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="center") position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
- contact.poly_contact.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
c_pin = self.get_pin("B") out_offset = vector(nmos_pin.cx() + self.route_layer_pitch,
out_offset = vector(c_pin.cx() + self.m1_pitch, output_yoffset)
self.inputA_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
@ -251,27 +236,22 @@ class pnand2(pgate.pgate):
# [top_pin_offset, mid1_offset, out_offset, # [top_pin_offset, mid1_offset, out_offset,
# mid2_offset, bottom_pin_offset]) # mid2_offset, bottom_pin_offset])
# This routes on M1 # 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)
# Midpoints of the L routes goes horizontal first then vertical # Top transistors
mid2_offset = vector(out_offset.x, bottom_pin_offset.y) self.add_path(self.route_layer,
[top_pin_offset, top_mid_offset, out_offset])
self.add_path("m1", bottom_mid_offset = bottom_pin_offset + vector(0, self.route_layer_pitch)
[top_pin_offset, mid1_offset, out_offset]) # Bottom transistors
# Route in two segments to have the width rule self.add_path(self.route_layer,
self.add_path("m1", [out_offset, bottom_mid_offset, bottom_pin_offset])
[bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
width=nmos_pin.height())
self.add_path("m1",
[mid2_offset, out_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",
layer="m1", layer=self.route_layer,
offset=out_offset, offset=out_offset)
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""

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.
@ -74,68 +74,64 @@ class pnand3(pgate.pgate):
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos_nsnd = factory.create(module_type="ptx", self.nmos_center = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact="active")
self.add_mod(self.nmos_center)
self.nmos_right = factory.create(module_type="ptx",
width=self.nmos_width,
mults=self.tx_mults,
tx_type="nmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
self.add_mod(self.nmos_right)
self.nmos_left = factory.create(module_type="ptx",
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=False, add_source_contact=self.route_layer,
add_drain_contact=False, add_drain_contact="active")
connect_poly=True, self.add_mod(self.nmos_left)
connect_active=True)
self.add_mod(self.nmos_nsnd)
self.nmos_ns = factory.create(module_type="ptx", self.pmos_left = factory.create(module_type="ptx",
width=self.nmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="pmos",
add_source_contact=False, add_source_contact=self.route_layer,
connect_poly=True, add_drain_contact=self.route_layer)
connect_active=True) self.add_mod(self.pmos_left)
self.add_mod(self.nmos_ns)
self.nmos_nd = factory.create(module_type="ptx", self.pmos_center = factory.create(module_type="ptx",
width=self.nmos_width, width=self.pmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="pmos",
add_drain_contact=False, add_source_contact=self.route_layer,
connect_poly=True, add_drain_contact=self.route_layer)
connect_active=True) self.add_mod(self.pmos_center)
self.add_mod(self.nmos_nd)
self.pmos = 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",
connect_poly=True, add_source_contact=self.route_layer,
connect_active=True) add_drain_contact=self.route_layer)
self.add_mod(self.pmos) 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. """
# Compute the overlap of the source and drain pins # Compute the overlap of the source and drain pins
overlap_xoffset = self.pmos.get_pin("D").ll().x - self.pmos.get_pin("S").ll().x self.ptx_offset = self.pmos_left.get_pin("D").center() - self.pmos_left.get_pin("S").center()
self.ptx_offset = vector(overlap_xoffset, 0)
# This is the extra space needed to ensure DRC rules # This is the extra space needed to ensure DRC rules
# 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):
""" """
@ -143,27 +139,27 @@ class pnand3(pgate.pgate):
""" """
self.pmos1_inst = self.add_inst(name="pnand3_pmos1", self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos) mod=self.pmos_left)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2", self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
mod=self.pmos) mod=self.pmos_center)
self.connect_inst(["Z", "B", "vdd", "vdd"]) self.connect_inst(["Z", "B", "vdd", "vdd"])
self.pmos3_inst = self.add_inst(name="pnand3_pmos3", self.pmos3_inst = self.add_inst(name="pnand3_pmos3",
mod=self.pmos) mod=self.pmos_right)
self.connect_inst(["Z", "C", "vdd", "vdd"]) self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst = self.add_inst(name="pnand3_nmos1", self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos_nd) mod=self.nmos_left)
self.connect_inst(["Z", "C", "net1", "gnd"]) self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst = self.add_inst(name="pnand3_nmos2", self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos_nsnd) mod=self.nmos_center)
self.connect_inst(["net1", "B", "net2", "gnd"]) self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst = self.add_inst(name="pnand3_nmos3", self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos_ns) mod=self.nmos_right)
self.connect_inst(["net2", "A", "gnd", "gnd"]) self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
@ -172,8 +168,8 @@ class pnand3(pgate.pgate):
and lowest position to provide maximum routing in channel and lowest position to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, pmos1_pos = vector(self.pmos_left.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space) self.height - self.pmos_left.active_height - self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos) self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.ptx_offset pmos2_pos = pmos1_pos + self.ptx_offset
@ -182,7 +178,7 @@ class pnand3(pgate.pgate):
self.pmos3_pos = pmos2_pos + self.ptx_offset self.pmos3_pos = pmos2_pos + self.ptx_offset
self.pmos3_inst.place(self.pmos3_pos) self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x, nmos1_pos = vector(self.pmos_left.active_offset.x,
self.top_bottom_space) self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) self.nmos1_inst.place(nmos1_pos)
@ -195,9 +191,9 @@ class pnand3(pgate.pgate):
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 """
self.add_nwell_contact(self.pmos, self.add_nwell_contact(self.pmos_right,
self.pmos3_pos + vector(self.m1_pitch, 0)) self.pmos3_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_ns, self.add_pwell_contact(self.nmos_right,
self.nmos3_pos + vector(self.m1_pitch, 0)) self.nmos3_pos + vector(self.m1_pitch, 0))
def connect_rails(self): def connect_rails(self):
@ -212,34 +208,31 @@ 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()
self.output_yoffset = pmos_drain_bottom - 0.5 * self.route_layer_width - self.route_layer_space
self.inputA_yoffset = self.output_yoffset - 0.5 * self.route_layer_width - self.route_layer_space
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="left")
# Put B right on the well line # Put B right on the well line
self.inputB_yoffset = self.nwell_y_offset self.inputB_yoffset = self.inputA_yoffset - self.m1_pitch
self.route_input_gate(self.pmos2_inst, self.route_input_gate(self.pmos2_inst,
self.nmos2_inst, self.nmos2_inst,
self.inputB_yoffset, self.inputB_yoffset,
"B", "B",
position="center") position="center")
# FIXME: constant hack self.inputC_yoffset = self.inputB_yoffset - self.m1_pitch
self.inputC_yoffset = self.inputB_yoffset - 1.15 * m1_pitch
self.route_input_gate(self.pmos3_inst, self.route_input_gate(self.pmos3_inst,
self.nmos3_inst, self.nmos3_inst,
self.inputC_yoffset, self.inputC_yoffset,
"C", "C",
position="right") position="right")
# FIXME: constant hack
if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="left")
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
@ -250,13 +243,8 @@ class pnand3(pgate.pgate):
# NMOS3 drain # NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# midpoint for routing out_offset = vector(nmos3_pin.cx() + self.route_layer_pitch,
mid_offset = vector(nmos3_pin.cx() + self.m1_pitch, self.output_yoffset)
self.inputA_yoffset)
# Aligned with the well taps
out_offset = vector(self.nwell_contact.cx(),
self.inputA_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,
@ -282,26 +270,24 @@ class pnand3(pgate.pgate):
bottom_pin_offset = nmos3_pin.center() bottom_pin_offset = nmos3_pin.center()
# PMOS1 to output # PMOS1 to output
self.add_path("m1", [top_left_pin_offset, self.add_path(self.route_layer, [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y), vector(top_left_pin_offset.x, out_offset.y),
out_offset]) out_offset])
# PMOS3 to output # PMOS3 to output
self.add_path("m1", [top_right_pin_offset, self.add_path(self.route_layer, [top_right_pin_offset,
vector(top_right_pin_offset.x, mid_offset.y), vector(top_right_pin_offset.x, out_offset.y),
mid_offset]) out_offset])
# NMOS3 to output # NMOS3 to output
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_path("m1", self.add_path(self.route_layer,
[bottom_pin_offset, mid2_offset], [bottom_pin_offset, mid2_offset],
width=nmos3_pin.height()) width=nmos3_pin.height())
mid3_offset = vector(mid_offset.x, nmos3_pin.by()) mid3_offset = vector(out_offset.x, nmos3_pin.by())
self.add_path("m1", [mid3_offset, mid_offset]) self.add_path(self.route_layer, [mid3_offset, out_offset])
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer=self.route_layer,
offset=out_offset, offset=out_offset)
width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""

View File

@ -71,74 +71,54 @@ class pnor2(pgate.pgate):
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_left = factory.create(module_type="ptx",
width=self.nmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="nmos", tx_type="nmos",
connect_poly=True, add_source_contact=self.route_layer,
connect_active=True) add_drain_contact=self.route_layer)
self.add_mod(self.nmos) self.add_mod(self.nmos_left)
self.pmos_nd = factory.create(module_type="ptx", self.nmos_right = factory.create(module_type="ptx",
width=self.pmos_width, width=self.nmos_width,
mults=self.tx_mults, mults=self.tx_mults,
tx_type="pmos", tx_type="nmos",
add_drain_contact=False, add_source_contact=self.route_layer,
connect_poly=True, add_drain_contact=self.route_layer)
connect_active=True) self.add_mod(self.nmos_right)
self.add_mod(self.pmos_nd)
self.pmos_ns = 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=False, add_source_contact=self.route_layer,
connect_poly=True, add_drain_contact="active")
connect_active=True) self.add_mod(self.pmos_left)
self.add_mod(self.pmos_ns)
self.pmos_right = factory.create(module_type="ptx",
width=self.pmos_width,
mults=self.tx_mults,
tx_type="pmos",
add_source_contact="active",
add_drain_contact=self.route_layer)
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, 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_ns.get_pin("D").ll() - self.pmos_nd.get_pin("S").ll() self.overlap_offset = self.pmos_right.get_pin("D").center() - self.pmos_left.get_pin("S").center()
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.width = 2 * self.pmos_ns.active_width \ self.width = 2 * self.pmos_right.active_width \
+ self.pmos_ns.active_contact.width \ + self.pmos_right.active_contact.width \
+ 2 * self.active_space \ + 2 * self.active_space \
+ 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.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
@ -146,19 +126,19 @@ class pnor2(pgate.pgate):
""" """
self.pmos1_inst = self.add_inst(name="pnor2_pmos1", self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos_nd) mod=self.pmos_left)
self.connect_inst(["vdd", "A", "net1", "vdd"]) self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2", self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
mod=self.pmos_ns) mod=self.pmos_right)
self.connect_inst(["net1", "B", "Z", "vdd"]) self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst = self.add_inst(name="pnor2_nmos1", self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos) mod=self.nmos_left)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst = self.add_inst(name="pnor2_nmos2", self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos) mod=self.nmos_right)
self.connect_inst(["Z", "B", "gnd", "gnd"]) self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
@ -166,30 +146,35 @@ 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_ns.active_offset.x, pmos1_pos = vector(self.pmos_right.active_offset.x,
self.height - self.pmos_ns.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
self.pmos2_inst.place(self.pmos2_pos) self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos_ns.active_offset.x, self.top_bottom_space) nmos1_pos = vector(self.pmos_right.active_offset.x, self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) self.nmos1_inst.place(nmos1_pos)
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)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
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 """
self.add_nwell_contact(self.pmos_ns, self.pmos2_pos) self.add_nwell_contact(self.pmos_right, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos) self.add_pwell_contact(self.nmos_right, self.nmos2_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
@ -202,53 +187,53 @@ 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="center") 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.m1_nonpref_pitch
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS2 drain # PMOS2 (right) drain
pmos_pin = self.pmos2_inst.get_pin("D") pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain # NMOS1 (left) drain
nmos_pin = self.nmos1_inst.get_pin("D") nmos_pin = self.nmos1_inst.get_pin("D")
# NMOS2 drain (for output via placement) # NMOS2 (right) drain (for output via placement)
nmos2_pin = self.nmos2_inst.get_pin("D") nmos2_pin = self.nmos2_inst.get_pin("D")
# 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,
offset=pmos_pin.center()) # offset=pmos_pin.center())
m1m2_contact = self.add_via_center(layers=self.m1_stack, # m1m2_contact = self.add_via_center(layers=self.m1_stack,
offset=nmos_pin.center()) # offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y) mid1_offset = vector(nmos_pin.center().x, self.output_yoffset)
mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset) mid2_offset = vector(pmos_pin.center().x, self.output_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain # PMOS1 to mid-drain to NMOS2 drain
self.add_path("m2", self.add_path(self.route_layer,
[pmos_pin.center(), mid2_offset, mid3_offset]) [nmos_pin.center(), mid1_offset, mid2_offset, pmos_pin.center()])
self.add_path("m2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=self.m1_stack,
offset=mid3_offset)
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer=self.route_layer,
offset=mid3_offset, offset=mid2_offset)
width=contact.m1_via.first_layer_height,
height=contact.m1_via.first_layer_width)
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""

View File

@ -81,7 +81,7 @@ class precharge(design.design):
Initializes the upper and lower pmos Initializes the upper and lower pmos
""" """
if(OPTS.tech_name == "s8"): if(OPTS.tech_name == "s8"):
(self.ptx_width, self.ptx_mults) = pgate.bin_width(self, "pmos", self.ptx_width) (self.ptx_width, self.ptx_mults) = pgate.bin_width("pmos", self.ptx_width)
self.pmos = factory.create(module_type="ptx", self.pmos = factory.create(module_type="ptx",
width=self.ptx_width, width=self.ptx_width,
mults=self.ptx_mults, mults=self.ptx_mults,
@ -114,7 +114,7 @@ class precharge(design.design):
self.add_power_pin("vdd", self.add_power_pin("vdd",
self.well_contact_pos, self.well_contact_pos,
vertical=True) directions=("V", "V"))
# Hack for li layers # Hack for li layers
if hasattr(self, "li_stack"): if hasattr(self, "li_stack"):
@ -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

@ -14,7 +14,7 @@ import contact
import logical_effort import logical_effort
import os import os
from globals import OPTS from globals import OPTS
from pgate import pgate
class ptx(design.design): class ptx(design.design):
""" """
@ -23,32 +23,47 @@ class ptx(design.design):
the transistor width. Mults is the number of transistors of the the transistor width. Mults is the number of transistors of the
given width. Total width is therefore mults*width. Options allow given width. Total width is therefore mults*width. Options allow
you to connect the fingered gates and active for parallel devices. you to connect the fingered gates and active for parallel devices.
The add_*_contact option tells which layer to bring source/drain up to.
""" """
def __init__(self, def __init__(self,
name="", name="",
width=drc("minwidth_tx"), width=drc("minwidth_tx"),
mults=1, mults=1,
tx_type="nmos", tx_type="nmos",
add_source_contact=True, add_source_contact=None,
add_drain_contact=True, add_drain_contact=None,
series_devices=False, series_devices=False,
connect_active=False, connect_drain_active=False,
connect_source_active=False,
connect_poly=False, connect_poly=False,
num_contacts=None): num_contacts=None):
if "li" in layer:
self.route_layer = "li"
else:
self.route_layer = "m1"
# Default contacts are the lowest layer
if not add_source_contact:
add_source_contact = self.route_layer
# Default contacts are the lowest layer
if not add_drain_contact:
add_drain_contact = self.route_layer
# We need to keep unique names because outputting to GDSII # 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
# over-write a design in GDS if one has and the other doesn't # over-write a design in GDS if one has and the other doesn't
# have poly connected, for example. # have poly connected, for example.
name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width) name = "{0}_m{1}_w{2:.3f}".format(tx_type, mults, width)
if not add_source_contact: name += "_s{}".format(add_source_contact)
name += "_ns" name += "_d{}".format(add_drain_contact)
if not add_drain_contact:
name += "_nd"
if series_devices: if series_devices:
name += "_sd" name += "_sd"
if connect_active: if connect_drain_active:
name += "_a" name += "_da"
if connect_source_active:
name += "_sa"
if connect_poly: if connect_poly:
name += "_p" name += "_p"
if num_contacts: if num_contacts:
@ -61,13 +76,17 @@ class ptx(design.design):
self.tx_type = tx_type self.tx_type = tx_type
self.mults = mults self.mults = mults
self.tx_width = width self.tx_width = width
self.connect_active = connect_active self.connect_drain_active = connect_drain_active
self.connect_source_active = connect_source_active
self.connect_poly = connect_poly self.connect_poly = connect_poly
self.add_source_contact = add_source_contact self.add_source_contact = add_source_contact
self.add_drain_contact = add_drain_contact self.add_drain_contact = add_drain_contact
self.series_devices = series_devices self.series_devices = series_devices
self.num_contacts = num_contacts self.num_contacts = num_contacts
self.route_layer_width = drc("minwidth_{}".format(self.route_layer))
self.route_layer_space = drc("{0}_to_{0}".format(self.route_layer))
# Since it has variable height, it is not a pgate. # Since it has variable height, it is not a pgate.
self.create_netlist() self.create_netlist()
# We must always create ptx layout for pbitcell # We must always create ptx layout for pbitcell
@ -109,6 +128,7 @@ class ptx(design.design):
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
if OPTS.tech_name == "s8": if OPTS.tech_name == "s8":
# s8 technology is in microns # s8 technology is in microns
(self.tx_width, self.mults) = pgate.bin_width(self.tx_type, self.tx_width)
main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,
@ -265,54 +285,41 @@ class ptx(design.design):
width=poly_width, width=poly_width,
height=self.poly_width) height=self.poly_width)
def connect_fingered_active(self, drain_positions, source_positions): 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 len(positions) <= 1:
return
layer_space = getattr(self, "{}_space".format(self.route_layer))
layer_width = getattr(self, "{}_width".format(self.route_layer))
# This is the distance that we must route up or down from the center # 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
pin_offset = vector(0, pin_offset = vector(0,
0.5 * self.active_contact.second_layer_height + self.m1_space + 0.5 * self.m1_width) 0.5 * self.active_contact.second_layer_height + layer_space + 0.5 * layer_width)
# 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(self.m1_width / 2.0, 0) end_offset = vector(layer_width / 2.0, 0)
# drains always go to the MIDDLE of the cell, # We move the opposite direction from the bottom
# so top of NMOS, bottom of PMOS if not top:
# so reverse the directions for NMOS compared to PMOS. offset = pin_offset.scale(-1, -1)
if self.tx_type == "pmos":
drain_dir = -1
source_dir = 1
else: else:
drain_dir = 1 offset = pin_offset
source_dir = -1
if len(source_positions) > 1: # remove the individual connections
source_offset = pin_offset.scale(source_dir, source_dir) self.remove_layout_pin(pin_name)
# remove the individual connections # Add each vertical segment
self.remove_layout_pin("S") for a in positions:
# Add each vertical segment self.add_path(self.route_layer,
for a in source_positions: [a, a + offset])
self.add_path(("m1"), # Add a single horizontal pin
[a, a + pin_offset.scale(source_dir, self.add_layout_pin_segment_center(text=pin_name,
source_dir)]) layer=self.route_layer,
# Add a single horizontal pin start=positions[0] + offset - end_offset,
self.add_layout_pin_segment_center(text="S", end=positions[-1] + offset + end_offset)
layer="m1",
start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1] + source_offset + end_offset)
if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir)
self.remove_layout_pin("D") # remove the individual connections
# Add each vertical segment
for a in drain_positions:
self.add_path(("m1"), [a,a+drain_offset])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="D",
layer="m1",
start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self): def add_poly(self):
""" """
@ -432,12 +439,12 @@ class ptx(design.design):
label = "S" label = "S"
source_positions.append(pos) source_positions.append(pos)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): if (label=="S" and self.add_source_contact):
contact = self.add_diff_contact(label, pos) contact = self.add_diff_contact(label, pos)
if label == "S": self.source_contacts.append(contact)
self.source_contacts.append(contact) elif (label=="D" and self.add_drain_contact):
else: contact = self.add_diff_contact(label, pos)
self.drain_contacts.append(contact) self.drain_contacts.append(contact)
else: else:
self.add_layout_pin_rect_center(text=label, self.add_layout_pin_rect_center(text=label,
layer="active", layer="active",
@ -453,19 +460,22 @@ class ptx(design.design):
label = "S" label = "S"
source_positions.append(pos) source_positions.append(pos)
if (label=="S" and self.add_source_contact) or (label=="D" and self.add_drain_contact): if (label=="S" and self.add_source_contact):
contact = self.add_diff_contact(label, pos) contact = self.add_diff_contact(label, pos)
if label == "S": self.source_contacts.append(contact)
self.source_contacts.append(contact) elif (label=="D" and self.add_drain_contact):
else: contact = self.add_diff_contact(label, pos)
self.drain_contacts.append(contact) self.drain_contacts.append(contact)
else: else:
self.add_layout_pin_rect_center(text=label, self.add_layout_pin_rect_center(text=label,
layer="active", layer="active",
offset=pos) offset=pos)
if self.connect_active: if self.connect_source_active:
self.connect_fingered_active(drain_positions, source_positions) self.connect_fingered_active(source_positions, "S", top=(self.tx_type=="pmos"))
if self.connect_drain_active:
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."""
@ -488,34 +498,39 @@ class ptx(design.design):
return self.mults * self.tx_width / drc("minwidth_tx") return self.mults * self.tx_width / drc("minwidth_tx")
def add_diff_contact(self, label, pos): def add_diff_contact(self, label, pos):
contact=self.add_via_center(layers=self.active_stack,
offset=pos,
size=(1, self.num_contacts),
directions=("V", "V"),
implant_type=self.implant_type,
well_type=self.well_type)
if hasattr(self, "li_stack"): if label == "S":
contact=self.add_via_center(layers=self.li_stack, layer = self.add_source_contact
offset=pos, elif label == "D":
directions=("V", "V")) layer = self.add_drain_contact
else:
debug.error("Invalid source drain name.")
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height if layer != "active":
# min_area = drc("minarea_m1") via=self.add_via_stack_center(offset=pos,
# width = contact.mod.second_layer_width from_layer="active",
# if contact_area < min_area: to_layer=layer,
# height = min_area / width size=(1, self.num_contacts),
# else: directions=("V", "V"),
# height = contact.mod.second_layer_height implant_type=self.implant_type,
width = contact.mod.second_layer_width well_type=self.well_type)
height = contact.mod.second_layer_height
pin_height = via.mod.second_layer_height
pin_width = via.mod.second_layer_width
else:
via = None
pin_height = None
pin_width = None
# Source drain vias are all vertical
self.add_layout_pin_rect_center(text=label, self.add_layout_pin_rect_center(text=label,
layer="m1", layer=layer,
offset=pos, offset=pos,
width=width, width=pin_width,
height=height) height=pin_height)
return(contact) return(via)
def get_cin(self): def get_cin(self):
"""Returns the relative gate cin of the tx""" """Returns the relative gate cin of the tx"""

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

@ -5,21 +5,15 @@
# (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 gdsMill
import tech
import math
import debug import debug
from globals import OPTS,print_time from globals import print_time
from contact import contact
from pin_group import pin_group
from pin_layout import pin_layout
from vector3d import vector3d from vector3d import vector3d
from router import router from router import router
from direction import direction from direction import direction
from datetime import datetime from datetime import datetime
import grid
import grid_utils import grid_utils
class supply_grid_router(router): class supply_grid_router(router):
""" """
A router class to read an obstruction map from a gds and A router class to read an obstruction map from a gds and
@ -45,13 +39,12 @@ class supply_grid_router(router):
print_time("Init supply router", datetime.now(), start_time, 3) print_time("Init supply router", datetime.now(), start_time, 3)
def create_routing_grid(self): def create_routing_grid(self):
""" """
Create a sprase routing grid with A* expansion functions. Create a sprase routing grid with A* expansion functions.
""" """
size = self.ur - self.ll size = self.ur - self.ll
debug.info(1,"Size: {0} x {1}".format(size.x,size.y)) debug.info(1, "Size: {0} x {1}".format(size.x, size.y))
import supply_grid import supply_grid
self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width) self.rg = supply_grid.supply_grid(self.ll, self.ur, self.track_width)
@ -60,12 +53,12 @@ class supply_grid_router(router):
""" """
Add power supply rails and connect all pins to these rails. Add power supply rails and connect all pins to these rails.
""" """
debug.info(1,"Running supply router on {0} and {1}...".format(vdd_name, gnd_name)) debug.info(1, "Running supply router on {0} and {1}...".format(vdd_name, gnd_name))
self.vdd_name = vdd_name self.vdd_name = vdd_name
self.gnd_name = gnd_name self.gnd_name = gnd_name
# Clear the pins if we have previously routed # Clear the pins if we have previously routed
if (hasattr(self,'rg')): if (hasattr(self, 'rg')):
self.clear_pins() self.clear_pins()
else: else:
# Creat a routing grid over the entire area # Creat a routing grid over the entire area
@ -76,32 +69,32 @@ class supply_grid_router(router):
# Get the pin shapes # Get the pin shapes
start_time = datetime.now() start_time = datetime.now()
self.find_pins_and_blockages([self.vdd_name, self.gnd_name]) self.find_pins_and_blockages([self.vdd_name, self.gnd_name])
print_time("Finding pins and blockages",datetime.now(), start_time, 3) print_time("Finding pins and blockages", datetime.now(), start_time, 3)
# Add the supply rails in a mesh network and connect H/V with vias # Add the supply rails in a mesh network and connect H/V with vias
start_time = datetime.now() start_time = datetime.now()
# Block everything # Block everything
self.prepare_blockages(self.gnd_name) self.prepare_blockages(self.gnd_name)
# Determine the rail locations # Determine the rail locations
self.route_supply_rails(self.gnd_name,0) self.route_supply_rails(self.gnd_name, 0)
# Block everything # Block everything
self.prepare_blockages(self.vdd_name) self.prepare_blockages(self.vdd_name)
# Determine the rail locations # Determine the rail locations
self.route_supply_rails(self.vdd_name,1) self.route_supply_rails(self.vdd_name, 1)
print_time("Routing supply rails",datetime.now(), start_time, 3) print_time("Routing supply rails", datetime.now(), start_time, 3)
start_time = datetime.now() start_time = datetime.now()
self.route_simple_overlaps(vdd_name) self.route_simple_overlaps(vdd_name)
self.route_simple_overlaps(gnd_name) self.route_simple_overlaps(gnd_name)
print_time("Simple overlap routing",datetime.now(), start_time, 3) print_time("Simple overlap routing", datetime.now(), start_time, 3)
# Route the supply pins to the supply rails # Route the supply pins to the supply rails
# Route vdd first since we want it to be shorter # Route vdd first since we want it to be shorter
start_time = datetime.now() start_time = datetime.now()
self.route_pins_to_rails(vdd_name) self.route_pins_to_rails(vdd_name)
self.route_pins_to_rails(gnd_name) self.route_pins_to_rails(gnd_name)
print_time("Maze routing supplies",datetime.now(), start_time, 3) print_time("Maze routing supplies", datetime.now(), start_time, 3)
#self.write_debug_gds("final.gds",False) # self.write_debug_gds("final.gds", False)
# Did we route everything?? # Did we route everything??
if not self.check_all_routed(vdd_name): if not self.check_all_routed(vdd_name):
@ -111,7 +104,6 @@ class supply_grid_router(router):
return True return True
def check_all_routed(self, pin_name): def check_all_routed(self, pin_name):
""" """
Check that all pin groups are routed. Check that all pin groups are routed.
@ -125,7 +117,7 @@ class supply_grid_router(router):
This checks for simple cases where a pin component already overlaps a supply rail. This checks for simple cases where a pin component already overlaps a supply rail.
It will add an enclosure to ensure the overlap in wide DRC rule cases. It will add an enclosure to ensure the overlap in wide DRC rule cases.
""" """
debug.info(1,"Routing simple overlap pins for {0}".format(pin_name)) debug.info(1, "Routing simple overlap pins for {0}".format(pin_name))
# These are the wire tracks # These are the wire tracks
wire_tracks = self.supply_rail_tracks[pin_name] wire_tracks = self.supply_rail_tracks[pin_name]
@ -142,10 +134,10 @@ class supply_grid_router(router):
continue continue
# Else, if we overlap some of the space track, we can patch it with an enclosure # Else, if we overlap some of the space track, we can patch it with an enclosure
#pg.create_simple_overlap_enclosure(pg.grids) # pg.create_simple_overlap_enclosure(pg.grids)
#pg.add_enclosure(self.cell) # pg.add_enclosure(self.cell)
debug.info(1,"Routed {} simple overlap pins".format(routed_count)) debug.info(1, "Routed {} simple overlap pins".format(routed_count))
def finalize_supply_rails(self, name): def finalize_supply_rails(self, name):
""" """
@ -158,7 +150,7 @@ class supply_grid_router(router):
connections = set() connections = set()
via_areas = [] via_areas = []
for i1,r1 in enumerate(all_rails): for i1, r1 in enumerate(all_rails):
# Only consider r1 horizontal rails # Only consider r1 horizontal rails
e = next(iter(r1)) e = next(iter(r1))
if e.z==1: if e.z==1:
@ -166,9 +158,9 @@ class supply_grid_router(router):
# We need to move this rail to the other layer for the z indices to match # We need to move this rail to the other layer for the z indices to match
# during the intersection. This also makes a copy. # during the intersection. This also makes a copy.
new_r1 = {vector3d(i.x,i.y,1) for i in r1} new_r1 = {vector3d(i.x, i.y, 1) for i in r1}
for i2,r2 in enumerate(all_rails): for i2, r2 in enumerate(all_rails):
# Never compare to yourself # Never compare to yourself
if i1==i2: if i1==i2:
continue continue
@ -184,16 +176,16 @@ class supply_grid_router(router):
# the overlap area for placement of a via # the overlap area for placement of a via
overlap = new_r1 & r2 overlap = new_r1 & r2
if len(overlap) >= 1: if len(overlap) >= 1:
debug.info(3,"Via overlap {0} {1}".format(len(overlap),overlap)) debug.info(3, "Via overlap {0} {1}".format(len(overlap),overlap))
connections.update([i1,i2]) connections.update([i1, i2])
via_areas.append(overlap) via_areas.append(overlap)
# Go through and add the vias at the center of the intersection # Go through and add the vias at the center of the intersection
for area in via_areas: for area in via_areas:
ll = grid_utils.get_lower_left(area) ll = grid_utils.get_lower_left(area)
ur = grid_utils.get_upper_right(area) ur = grid_utils.get_upper_right(area)
center = (ll + ur).scale(0.5,0.5,0) center = (ll + ur).scale(0.5, 0.5, 0)
self.add_via(center,1) self.add_via(center, 1)
# Determien which indices were not connected to anything above # Determien which indices were not connected to anything above
missing_indices = set([x for x in range(len(self.supply_rails[name]))]) missing_indices = set([x for x in range(len(self.supply_rails[name]))])
@ -204,14 +196,13 @@ class supply_grid_router(router):
for rail_index in sorted(missing_indices, reverse=True): for rail_index in sorted(missing_indices, reverse=True):
ll = grid_utils.get_lower_left(all_rails[rail_index]) ll = grid_utils.get_lower_left(all_rails[rail_index])
ur = grid_utils.get_upper_right(all_rails[rail_index]) ur = grid_utils.get_upper_right(all_rails[rail_index])
debug.info(1,"Removing disconnected supply rail {0} .. {1}".format(ll,ur)) debug.info(1, "Removing disconnected supply rail {0} .. {1}".format(ll, ur))
self.supply_rails[name].pop(rail_index) self.supply_rails[name].pop(rail_index)
# Make the supply rails into a big giant set of grids for easy blockages. # Make the supply rails into a big giant set of grids for easy blockages.
# Must be done after we determine which ones are connected. # Must be done after we determine which ones are connected.
self.create_supply_track_set(name) self.create_supply_track_set(name)
def add_supply_rails(self, name): def add_supply_rails(self, name):
""" """
Add the shapes that represent the routed supply rails. Add the shapes that represent the routed supply rails.
@ -223,7 +214,7 @@ class supply_grid_router(router):
ur = grid_utils.get_upper_right(rail) ur = grid_utils.get_upper_right(rail)
z = ll.z z = ll.z
pin = self.compute_pin_enclosure(ll, ur, z, name) pin = self.compute_pin_enclosure(ll, ur, z, name)
debug.info(3,"Adding supply rail {0} {1}->{2} {3}".format(name,ll,ur,pin)) debug.info(3, "Adding supply rail {0} {1}->{2} {3}".format(name, ll, ur, pin))
self.cell.add_layout_pin(text=name, self.cell.add_layout_pin(text=name,
layer=pin.layer, layer=pin.layer,
offset=pin.ll(), offset=pin.ll(),
@ -244,18 +235,17 @@ class supply_grid_router(router):
min_yoffset = self.rg.ll.y min_yoffset = self.rg.ll.y
min_xoffset = self.rg.ll.x min_xoffset = self.rg.ll.x
# Horizontal supply rails # Horizontal supply rails
start_offset = min_yoffset + supply_number start_offset = min_yoffset + supply_number
for offset in range(start_offset, max_yoffset, 2): for offset in range(start_offset, max_yoffset, 2):
# Seed the function at the location with the given width # Seed the function at the location with the given width
wave = [vector3d(min_xoffset,offset,0)] wave = [vector3d(min_xoffset, offset, 0)]
# While we can keep expanding east in this horizontal track # While we can keep expanding east in this horizontal track
while wave and wave[0].x < max_xoffset: while wave and wave[0].x < max_xoffset:
added_rail = self.find_supply_rail(name, wave, direction.EAST) added_rail = self.find_supply_rail(name, wave, direction.EAST)
if not added_rail: if not added_rail:
# Just seed with the next one # Just seed with the next one
wave = [x+vector3d(1,0,0) for x in wave] wave = [x+vector3d(1, 0, 0) for x in wave]
else: else:
# Seed with the neighbor of the end of the last rail # Seed with the neighbor of the end of the last rail
wave = added_rail.neighbor(direction.EAST) wave = added_rail.neighbor(direction.EAST)
@ -264,13 +254,13 @@ class supply_grid_router(router):
start_offset = min_xoffset + supply_number start_offset = min_xoffset + supply_number
for offset in range(start_offset, max_xoffset, 2): for offset in range(start_offset, max_xoffset, 2):
# Seed the function at the location with the given width # Seed the function at the location with the given width
wave = [vector3d(offset,min_yoffset,1)] wave = [vector3d(offset, min_yoffset, 1)]
# While we can keep expanding north in this vertical track # While we can keep expanding north in this vertical track
while wave and wave[0].y < max_yoffset: while wave and wave[0].y < max_yoffset:
added_rail = self.find_supply_rail(name, wave, direction.NORTH) added_rail = self.find_supply_rail(name, wave, direction.NORTH)
if not added_rail: if not added_rail:
# Just seed with the next one # Just seed with the next one
wave = [x+vector3d(0,1,0) for x in wave] wave = [x + vector3d(0, 1, 0) for x in wave]
else: else:
# Seed with the neighbor of the end of the last rail # Seed with the neighbor of the end of the last rail
wave = added_rail.neighbor(direction.NORTH) wave = added_rail.neighbor(direction.NORTH)
@ -295,7 +285,6 @@ class supply_grid_router(router):
# as it will be used to find the next start location # as it will be used to find the next start location
return wave_path return wave_path
def probe_supply_rail(self, name, start_wave, direct): def probe_supply_rail(self, name, start_wave, direct):
""" """
This finds the first valid starting location and routes a supply rail This finds the first valid starting location and routes a supply rail
@ -328,23 +317,19 @@ class supply_grid_router(router):
data structure. Return whether it was added or not. data structure. Return whether it was added or not.
""" """
# We must have at least 2 tracks to drop plus 2 tracks for a via # We must have at least 2 tracks to drop plus 2 tracks for a via
if len(wave_path)>=4*self.rail_track_width: if len(wave_path) >= 4 * self.rail_track_width:
grid_set = wave_path.get_grids() grid_set = wave_path.get_grids()
self.supply_rails[name].append(grid_set) self.supply_rails[name].append(grid_set)
return True return True
return False return False
def route_supply_rails(self, name, supply_number): def route_supply_rails(self, name, supply_number):
""" """
Route the horizontal and vertical supply rails across the entire design. Route the horizontal and vertical supply rails across the entire design.
Must be done with lower left at 0,0 Must be done with lower left at 0,0
""" """
debug.info(1,"Routing supply rail {0}.".format(name)) debug.info(1, "Routing supply rail {0}.".format(name))
# Compute the grid locations of the supply rails # Compute the grid locations of the supply rails
self.compute_supply_rails(name, supply_number) self.compute_supply_rails(name, supply_number)
@ -355,7 +340,6 @@ class supply_grid_router(router):
# Add the rails themselves # Add the rails themselves
self.add_supply_rails(name) self.add_supply_rails(name)
def create_supply_track_set(self, pin_name): def create_supply_track_set(self, pin_name):
""" """
Make a single set of all the tracks for the rail and wire itself. Make a single set of all the tracks for the rail and wire itself.
@ -365,8 +349,6 @@ class supply_grid_router(router):
rail_set.update(rail) rail_set.update(rail)
self.supply_rail_tracks[pin_name] = rail_set self.supply_rail_tracks[pin_name] = rail_set
def route_pins_to_rails(self, pin_name): def route_pins_to_rails(self, pin_name):
""" """
This will route each of the remaining pin components to the supply rails. This will route each of the remaining pin components to the supply rails.
@ -374,14 +356,14 @@ class supply_grid_router(router):
""" """
remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name]) remaining_components = sum(not x.is_routed() for x in self.pin_groups[pin_name])
debug.info(1,"Maze routing {0} with {1} pin components to connect.".format(pin_name, debug.info(1, "Maze routing {0} with {1} pin components to connect.".format(pin_name,
remaining_components)) remaining_components))
for index,pg in enumerate(self.pin_groups[pin_name]): for index, pg in enumerate(self.pin_groups[pin_name]):
if pg.is_routed(): if pg.is_routed():
continue continue
debug.info(3,"Routing component {0} {1}".format(pin_name, index)) debug.info(3, "Routing component {0} {1}".format(pin_name, index))
# Clear everything in the routing grid. # Clear everything in the routing grid.
self.rg.reinit() self.rg.reinit()
@ -400,28 +382,26 @@ class supply_grid_router(router):
# Actually run the A* router # Actually run the A* router
if not self.run_router(detour_scale=5): if not self.run_router(detour_scale=5):
self.write_debug_gds("debug_route.gds",False) self.write_debug_gds("debug_route.gds", False)
#if index==3 and pin_name=="vdd":
# self.write_debug_gds("route.gds",False)
# if index==3 and pin_name=="vdd":
# self.write_debug_gds("route.gds",False)
def add_supply_rail_target(self, pin_name): def add_supply_rail_target(self, pin_name):
""" """
Add the supply rails of given name as a routing target. Add the supply rails of given name as a routing target.
""" """
debug.info(4,"Add supply rail target {}".format(pin_name)) debug.info(4, "Add supply rail target {}".format(pin_name))
# Add the wire itself as the target # Add the wire itself as the target
self.rg.set_target(self.supply_rail_tracks[pin_name]) self.rg.set_target(self.supply_rail_tracks[pin_name])
# But unblock all the rail tracks including the space # But unblock all the rail tracks including the space
self.rg.set_blocked(self.supply_rail_tracks[pin_name],False) self.rg.set_blocked(self.supply_rail_tracks[pin_name], False)
def set_supply_rail_blocked(self, value=True): def set_supply_rail_blocked(self, value=True):
""" """
Add the supply rails of given name as a routing target. Add the supply rails of given name as a routing target.
""" """
debug.info(4,"Blocking supply rail") debug.info(4, "Blocking supply rail")
for rail_name in self.supply_rail_tracks: for rail_name in self.supply_rail_tracks:
self.rg.set_blocked(self.supply_rail_tracks[rail_name]) self.rg.set_blocked(self.supply_rail_tracks[rail_name])

View File

@ -59,13 +59,22 @@ 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
@ -497,70 +498,6 @@ class sram_base(design, verilog, lef):
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

@ -27,7 +27,8 @@ class ptx_3finger_nmos_test(openram_test):
width=tech.drc["minwidth_tx"], width=tech.drc["minwidth_tx"],
mults=3, mults=3,
tx_type="nmos", tx_type="nmos",
connect_active=True, connect_source_active=True,
connect_drain_active=True,
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)

View File

@ -27,7 +27,8 @@ class ptx_3finger_pmos_test(openram_test):
width=tech.drc["minwidth_tx"], width=tech.drc["minwidth_tx"],
mults=3, mults=3,
tx_type="pmos", tx_type="pmos",
connect_active=True, connect_source_active=True,
connect_drain_active=True,
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)

View File

@ -27,7 +27,8 @@ class ptx_4finger_nmos_test(openram_test):
width= tech.drc["minwidth_tx"], width= tech.drc["minwidth_tx"],
mults=4, mults=4,
tx_type="nmos", tx_type="nmos",
connect_active=True, connect_source_active=True,
connect_drain_active=True,
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)

View File

@ -27,7 +27,8 @@ class ptx_test(openram_test):
width=tech.drc["minwidth_tx"], width=tech.drc["minwidth_tx"],
mults=4, mults=4,
tx_type="pmos", tx_type="pmos",
connect_active=True, connect_source_active=True,
connect_drain_active=True,
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)

View File

@ -0,0 +1,60 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class ptx_no_contacts_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
import tech
debug.info(2, "Checking single finger no source/drain")
fet = factory.create(module_type="ptx",
width=tech.drc["minwidth_tx"],
mults=1,
add_source_contact=False,
add_drain_contact=False,
tx_type="nmos")
self.local_drc_check(fet)
debug.info(2, "Checking multifinger no source/drain")
fet = factory.create(module_type="ptx",
width=tech.drc["minwidth_tx"],
mults=4,
add_source_contact=False,
add_drain_contact=False,
tx_type="nmos")
self.local_drc_check(fet)
debug.info(2, "Checking series ptx")
fet = factory.create(module_type="ptx",
width=tech.drc["minwidth_tx"],
mults=4,
series_devices=True,
tx_type="nmos")
self.local_drc_check(fet)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys,os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class pinv_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(2, "Checking 100x inverter")
tx = factory.create(module_type="pinv", size=100)
self.local_check(tx)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

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

@ -0,0 +1,36 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class control_logic_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(1, "Testing sample for control_logic_r")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -0,0 +1,36 @@
#!/usr/bin/env python3
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import unittest
from testutils import *
import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class control_logic_test(openram_test):
def runTest(self):
config_file = "{}/tests/configs/config".format(os.getenv("OPENRAM_HOME"))
globals.init_openram(config_file)
debug.info(1, "Testing sample for control_logic_rw")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
self.local_check(a)
globals.end_openram()
# run the test from the command line
if __name__ == "__main__":
(OPTS, args) = globals.parse_args()
del sys.argv[1:]
header(__file__, OPTS.tech_name)
unittest.main(testRunner=debugTestRunner())

View File

@ -20,21 +20,13 @@ 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")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32)
self.local_check(a)
debug.info(1, "Testing sample for control_logic_r")
a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="r")
self.local_check(a)
debug.info(1, "Testing sample for control_logic_w") 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") a = factory.create(module_type="control_logic", num_rows=128, words_per_row=1, word_size=32, port_type="w")
self.local_check(a) self.local_check(a)
globals.end_openram()
# run the test from the command line # run the test from the command line
if __name__ == "__main__": if __name__ == "__main__":
(OPTS, args) = globals.parse_args() (OPTS, args) = globals.parse_args()

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]