Merge branch 'dev' into tech_migration

This commit is contained in:
mrg 2020-04-16 11:32:02 -07:00
commit 8ece411954
23 changed files with 791 additions and 538 deletions

View File

@ -74,7 +74,7 @@ class design(hierarchy_design):
return pitch return pitch
def setup_drc_constants(self): def setup_drc_constants(self):
""" """
These are some DRC constants used in many places These are some DRC constants used in many places
in the compiler. in the compiler.
""" """

15
compiler/base/errors.py Normal file
View File

@ -0,0 +1,15 @@
class drc_error(Exception):
"""Exception raised for DRC errors.
Attributes:
expression -- input expression in which the error occurred
message -- explanation of the error
"""
# def __init__(self, expression, message):
# self.expression = expression
# self.message = message
def __init__(self, message):
self.message = message

View File

@ -46,7 +46,6 @@ class layout():
except ImportError: except ImportError:
self.pwr_grid_layer = "m3" self.pwr_grid_layer = "m3"
############################################################ ############################################################
# GDS layout # GDS layout
############################################################ ############################################################
@ -196,7 +195,7 @@ class layout():
self.insts.append(geometry.instance(name, mod, offset, mirror, rotate)) self.insts.append(geometry.instance(name, mod, offset, mirror, rotate))
debug.info(3, "adding instance {}".format(self.insts[-1])) debug.info(3, "adding instance {}".format(self.insts[-1]))
# This is commented out for runtime reasons # This is commented out for runtime reasons
#debug.info(4, "instance list: " + ",".join(x.name for x in self.insts)) # debug.info(4, "instance list: " + ",".join(x.name for x in self.insts))
return self.insts[-1] return self.insts[-1]
def get_inst(self, name): def get_inst(self, name):
@ -214,12 +213,14 @@ class layout():
width = drc["minwidth_{}".format(layer)] width = drc["minwidth_{}".format(layer)]
if not height: if not height:
height = drc["minwidth_{}".format(layer)] height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer] lpp = techlayer[layer]
if lpp[0] >= 0: if abs(offset[0]-5.16250)<0.01 and abs(offset[1]-8.70750)<0.01:
self.objs.append(geometry.rectangle(lpp, offset, width, height)) import pdb; pdb.set_trace()
return self.objs[-1] self.objs.append(geometry.rectangle(lpp,
return None offset,
width,
height))
return self.objs[-1]
def add_rect_center(self, layer, offset, width=None, height=None): def add_rect_center(self, layer, offset, width=None, height=None):
""" """
@ -230,16 +231,13 @@ class layout():
width = drc["minwidth_{}".format(layer)] width = drc["minwidth_{}".format(layer)]
if not height: if not height:
height = drc["minwidth_{}".format(layer)] height = drc["minwidth_{}".format(layer)]
# negative layers indicate "unused" layers in a given technology
lpp = techlayer[layer] lpp = techlayer[layer]
corrected_offset = offset - vector(0.5 * width, 0.5 * height) corrected_offset = offset - vector(0.5 * width, 0.5 * height)
if lpp[0] >= 0: self.objs.append(geometry.rectangle(lpp,
self.objs.append(geometry.rectangle(lpp, corrected_offset,
corrected_offset, width,
width, height))
height)) return self.objs[-1]
return self.objs[-1]
return None
def add_segment_center(self, layer, start, end): def add_segment_center(self, layer, start, end):
""" """
@ -252,15 +250,15 @@ class layout():
elif start.x != end.x: elif start.x != end.x:
offset = vector(0, 0.5 * minwidth_layer) offset = vector(0, 0.5 * minwidth_layer)
return self.add_rect(layer, return self.add_rect(layer,
start-offset, start - offset,
end.x-start.x, end.x - start.x,
minwidth_layer) minwidth_layer)
else: else:
offset = vector(0.5 * minwidth_layer, 0) offset = vector(0.5 * minwidth_layer, 0)
return self.add_rect(layer, return self.add_rect(layer,
start-offset, start - offset,
minwidth_layer, minwidth_layer,
end.y-start.y) end.y - start.y)
def get_pin(self, text): def get_pin(self, text):
""" """
@ -268,14 +266,14 @@ class layout():
""" """
try: try:
if len(self.pin_map[text]) > 1: if len(self.pin_map[text]) > 1:
debug.error("Should use a pin iterator since more than one pin {}".format(text),-1) debug.error("Should use a pin iterator since more than one pin {}".format(text), -1)
# If we have one pin, return it and not the list. # If we have one pin, return it and not the list.
# Otherwise, should use get_pins() # Otherwise, should use get_pins()
any_pin = next(iter(self.pin_map[text])) any_pin = next(iter(self.pin_map[text]))
return any_pin return any_pin
except Exception: except Exception:
self.gds_write("missing_pin.gds") self.gds_write("missing_pin.gds")
debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text,self.name),-1) debug.error("No pin found with name {0} on {1}. Saved as missing_pin.gds.".format(text, self.name), -1)
def get_pins(self, text): def get_pins(self, text):
""" """
@ -377,7 +375,7 @@ class layout():
height = drc["minwidth_{0}".format(layer)] height = drc["minwidth_{0}".format(layer)]
new_pin = pin_layout(text, new_pin = pin_layout(text,
[offset, offset+vector(width, height)], [offset, offset + vector(width, height)],
layer) layer)
try: try:
@ -413,23 +411,18 @@ class layout():
def add_label(self, text, layer, offset=[0, 0], zoom=-1): def add_label(self, text, layer, offset=[0, 0], zoom=-1):
"""Adds a text label on the given layer,offset, and zoom level""" """Adds a text label on the given layer,offset, and zoom level"""
# negative layers indicate "unused" layers in a given technology
debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset)) debug.info(5, "add label " + str(text) + " " + layer + " " + str(offset))
lpp = techlayer[layer] lpp = techlayer[layer]
if lpp[0] >= 0: self.objs.append(geometry.label(text, lpp, offset, zoom))
self.objs.append(geometry.label(text, lpp, offset, zoom)) return self.objs[-1]
return self.objs[-1]
return None
def add_path(self, layer, coordinates, width=None): def add_path(self, layer, coordinates, width=None):
"""Connects a routing path on given layer,coordinates,width.""" """Connects a routing path on given layer,coordinates,width."""
debug.info(4, "add path " + str(layer) + " " + str(coordinates)) debug.info(4, "add path " + str(layer) + " " + str(coordinates))
import wire_path import wire_path
# NOTE: (UNTESTED) add_path(...) is currently not used # NOTE: (UNTESTED) add_path(...) is currently not used
# negative layers indicate "unused" layers in a given technology
# lpp = techlayer[layer] # lpp = techlayer[layer]
# if lpp[0] >= 0: # self.objs.append(geometry.path(lpp, coordinates, width))
# self.objs.append(geometry.path(lpp, coordinates, width))
wire_path.wire_path(obj=self, wire_path.wire_path(obj=self,
layer=layer, layer=layer,
@ -465,7 +458,7 @@ class layout():
from tech import preferred_directions from tech import preferred_directions
return preferred_directions[layer] return preferred_directions[layer]
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: if not directions:
@ -487,7 +480,7 @@ class layout():
self.connect_inst([]) self.connect_inst([])
return inst return inst
def add_via_center(self, layers, offset, directions=None, size=[1,1], implant_type=None, well_type=None): def add_via_center(self, layers, offset, directions=None, size=[1, 1], implant_type=None, well_type=None):
""" """
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.
@ -799,9 +792,7 @@ class layout():
self.add_rect(layer=layer, self.add_rect(layer=layer,
offset=line_offset, offset=line_offset,
height=length) height=length)
# Make this the center of the rail line_positions[names[i]] = line_offset + vector(half_minwidth, 0)
line_positions[names[i]] = line_offset + vector(half_minwidth,
0.5 * length)
else: else:
for i in range(len(names)): for i in range(len(names)):
line_offset = offset + vector(0, line_offset = offset + vector(0,
@ -953,14 +944,14 @@ class layout():
min_y = min([pin.center().y for pin in pins]) min_y = min([pin.center().y 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_y-min_y <= pitch: if max_y - min_y <= pitch:
half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)] half_layer_width = 0.5 * drc["minwidth_{0}".format(self.horizontal_layer)]
# Add the vertical trunk on the horizontal layer! # Add the vertical trunk on the horizontal layer!
self.add_path(self.horizontal_layer, self.add_path(self.horizontal_layer,
[vector(trunk_offset.x, min_y - half_layer_width), [vector(trunk_offset.x, min_y - half_layer_width),
vector(trunk_offset.x,max_y + half_layer_width)]) vector(trunk_offset.x, max_y + half_layer_width)])
# Route each pin to the trunk # Route each pin to the trunk
for pin in pins: for pin in pins:
@ -1000,7 +991,7 @@ class layout():
# Remove the pin from all conflicts # Remove the pin from all conflicts
# FIXME: This is O(n^2), so maybe optimize it. # FIXME: This is O(n^2), so maybe optimize it.
for other_pin,conflicts in g.items(): for other_pin, conflicts in g.items():
if pin in conflicts: if pin in conflicts:
conflicts.remove(pin) conflicts.remove(pin)
g[other_pin]=conflicts g[other_pin]=conflicts
@ -1151,8 +1142,8 @@ class layout():
else: else:
self.bounding_box = self.add_rect(layer=boundary_layer, self.bounding_box = self.add_rect(layer=boundary_layer,
offset=ll, offset=ll,
height=ur.y-ll.y, height=ur.y - ll.y,
width=ur.x-ll.x) width=ur.x - ll.x)
def add_enclosure(self, insts, layer="nwell"): def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful """ Add a layer that surrounds the given instances. Useful
@ -1171,8 +1162,8 @@ class layout():
self.add_rect(layer=layer, self.add_rect(layer=layer,
offset=vector(xmin, ymin), offset=vector(xmin, ymin),
width=xmax-xmin, width=xmax - xmin,
height=ymax-ymin) height=ymax - ymin)
def copy_power_pins(self, inst, name): def copy_power_pins(self, inst, name):
""" """
@ -1192,7 +1183,7 @@ class layout():
else: else:
debug.warning("{0} pins of {1} should be on {2} or metal1 for "\ debug.warning("{0} pins of {1} should be on {2} or metal1 for "\
"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], vertical=False, start_layer="m1"):
""" """
@ -1244,8 +1235,8 @@ class layout():
[ll, ur] = bbox [ll, ur] = bbox
supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width supply_rail_spacing = self.supply_rail_pitch - self.supply_rail_width
height = (ur.y-ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing height = (ur.y - ll.y) + 3 * self.supply_rail_pitch - supply_rail_spacing
width = (ur.x-ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing width = (ur.x - ll.x) + 3 * self.supply_rail_pitch - supply_rail_spacing
# LEFT vertical rails # LEFT vertical rails
offset = ll + vector(-2 * self.supply_rail_pitch, offset = ll + vector(-2 * self.supply_rail_pitch,

View File

@ -6,18 +6,19 @@
# All rights reserved. # All rights reserved.
# #
from tech import drc from tech import drc
import debug import contact
from wire_path import wire_path from wire_path import wire_path
from sram_factory import factory from sram_factory import factory
class wire(wire_path): class wire(wire_path):
""" """
Object metal wire; given the layer type Object metal wire; given the layer type
Add a wire of minimium metal width between a set of points. Add a wire of minimium metal width between a set of points.
The points should be rectilinear to control the bend points. If The points should be rectilinear to control the bend points. If
not, it will always go down first. 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.
""" """
def __init__(self, obj, layer_stack, position_list): def __init__(self, obj, layer_stack, position_list):
self.obj = obj self.obj = obj
@ -36,6 +37,7 @@ class wire(wire_path):
# wires and wire_paths should not be offset to (0,0) # wires and wire_paths should not be offset to (0,0)
def setup_layers(self): def setup_layers(self):
(horiz_layer, via_layer, vert_layer) = self.layer_stack (horiz_layer, via_layer, vert_layer) = self.layer_stack
self.via_layer_name = via_layer self.via_layer_name = via_layer
@ -47,21 +49,49 @@ class wire(wire_path):
via_connect = factory.create(module_type="contact", via_connect = factory.create(module_type="contact",
layer_stack=self.layer_stack, layer_stack=self.layer_stack,
dimensions=(1, 1)) dimensions=(1, 1))
# This is used for short connections to avoid via-to-via spacing errors
self.vert_layer_contact_width = max(via_connect.second_layer_width,
via_connect.first_layer_width)
self.horiz_layer_contact_width = max(via_connect.second_layer_height,
via_connect.first_layer_height)
self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width, self.node_to_node = [drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.width,
drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height] drc("minwidth_" + str(self.horiz_layer_name)) + via_connect.height]
self.pitch = self.compute_pitch(self.layer_stack)
def compute_pitch(self, layer_stack):
"""
This is contact direction independent pitch,
i.e. we take the maximum contact dimension
"""
(layer1, via, layer2) = layer_stack
if layer1 == "poly" or layer1 == "active":
contact1 = getattr(contact, layer1 + "_contact")
else:
try:
contact1 = getattr(contact, layer1 + "_via")
except AttributeError:
contact1 = getattr(contact, layer2 + "_via")
max_contact = max(contact1.width, contact1.height)
layer1_space = drc("{0}_to_{0}".format(layer1))
layer2_space = drc("{0}_to_{0}".format(layer2))
pitch = max_contact + max(layer1_space, layer2_space)
return pitch
# create a 1x1 contact # create a 1x1 contact
def create_vias(self): def create_vias(self):
""" Add a via and corner square at every corner of the path.""" """ Add a via and corner square at every corner of the path."""
self.c=factory.create(module_type="contact", self.c=factory.create(module_type="contact",
layer_stack=self.layer_stack, layer_stack=self.layer_stack,
dimensions=(1, 1)) dimensions=(1, 1))
c_width = self.c.width from itertools import tee, islice
c_height = self.c.height nwise = lambda g, n=2: zip(*(islice(g, i, None) for i, g in enumerate(tee(g, n))))
threewise = nwise(self.position_list, 3)
from itertools import tee,islice
nwise = lambda g,n=2: zip(*(islice(g,i,None) for i,g in enumerate(tee(g,n))))
threewise=nwise(self.position_list,3)
for (a, offset, c) in list(threewise): for (a, offset, c) in list(threewise):
# add a exceptions to prevent a via when we don't change directions # add a exceptions to prevent a via when we don't change directions
@ -72,18 +102,25 @@ class wire(wire_path):
self.obj.add_via_center(layers=self.layer_stack, self.obj.add_via_center(layers=self.layer_stack,
offset=offset) offset=offset)
def create_rectangles(self): def create_rectangles(self):
""" """
Create the actual rectangles on the appropriate layers Create the actual rectangles on the appropriate layers
using the position list of the corners. using the position list of the corners.
""" """
pl = self.position_list # position list pl = self.position_list # position list
for index in range(len(pl) - 1): for index in range(len(pl) - 1):
# Horizontal wire segment
if pl[index][0] != pl[index + 1][0]: if pl[index][0] != pl[index + 1][0]:
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
# 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:
width = self.horiz_layer_contact_width
else:
width = self.horiz_layer_width
temp_offset = [pl[index][0], temp_offset = [pl[index][0],
pl[index][1] - 0.5*self.horiz_layer_width] pl[index][1] - 0.5 * width]
# If we go in the negative direction, move the offset
if line_length < 0: if line_length < 0:
temp_offset = [temp_offset[0] + line_length, temp_offset = [temp_offset[0] + line_length,
temp_offset[1]] temp_offset[1]]
@ -91,10 +128,17 @@ class wire(wire_path):
length=abs(line_length), length=abs(line_length),
offset=temp_offset, offset=temp_offset,
orientation="horizontal", orientation="horizontal",
layer_width=self.horiz_layer_width) layer_width=width)
# Vertical wire segment
elif pl[index][1] != pl[index + 1][1]: elif pl[index][1] != pl[index + 1][1]:
line_length = pl[index + 1][1] - pl[index][1] line_length = pl[index + 1][1] - pl[index][1]
temp_offset = [pl[index][0] - 0.5 * self.vert_layer_width, # Make the wire wider to avoid via-to-via spacing problems
# But don't make it wider if it is shorter than one via
if abs(line_length) < self.pitch and abs(line_length) > self.vert_layer_contact_width:
width = self.vert_layer_contact_width
else:
width = self.vert_layer_width
temp_offset = [pl[index][0] - 0.5 * width,
pl[index][1]] pl[index][1]]
if line_length < 0: if line_length < 0:
temp_offset = [temp_offset[0], temp_offset = [temp_offset[0],
@ -103,11 +147,13 @@ class wire(wire_path):
length=abs(line_length), length=abs(line_length),
offset=temp_offset, offset=temp_offset,
orientation="vertical", orientation="vertical",
layer_width=self.vert_layer_width) layer_width=width)
def assert_node(self, A, B): def assert_node(self, A, B):
""" Check if the node movements are not big enough for the """
technology sizes.""" Check if the node movements are not big enough for the
technology sizes.
"""
X_diff = abs(A[0] - B[0]) X_diff = abs(A[0] - B[0])
Y_diff = abs(A[1] - B[1]) Y_diff = abs(A[1] - B[1])
[minX, minY] = self.node_to_node [minX, minY] = self.node_to_node

View File

@ -9,9 +9,10 @@ import debug
from drc_value import * from drc_value import *
from drc_lut import * from drc_lut import *
class design_rules(dict): class design_rules(dict):
""" """
This is a class that implements the design rules structures. This is a class that implements the design rules structures.
""" """
def __init__(self, name): def __init__(self, name):
self.tech_name = name self.tech_name = name

View File

@ -7,9 +7,10 @@
# #
import debug import debug
class drc_lut(): class drc_lut():
""" """
Implement a lookup table of rules. Implement a lookup table of rules.
Each element is a tuple with the last value being the rule. Each element is a tuple with the last value being the rule.
It searches through backwards until all of the key values are It searches through backwards until all of the key values are
met and returns the rule value. met and returns the rule value.
@ -31,7 +32,6 @@ class drc_lut():
for table_key in sorted(self.table.keys(), reverse=True): for table_key in sorted(self.table.keys(), reverse=True):
if self.match(key, table_key): if self.match(key, table_key):
return self.table[table_key] return self.table[table_key]
def match(self, key1, key2): def match(self, key1, key2):
""" """
@ -39,8 +39,8 @@ class drc_lut():
(i.e. return false if key1<key2 for any pair.) (i.e. return false if key1<key2 for any pair.)
""" """
# If any one pair is less than, return False # If any one pair is less than, return False
debug.check(len(key1)==len(key2),"Comparing invalid key lengths.") debug.check(len(key1) == len(key2), "Comparing invalid key lengths.")
for k1,k2 in zip(key1,key2): for k1, k2 in zip(key1, key2):
if k1 < k2: if k1 < k2:
return False return False
return True return True

View File

@ -6,8 +6,9 @@
# All rights reserved. # All rights reserved.
# #
class drc_value(): class drc_value():
""" """
A single DRC value. A single DRC value.
""" """
def __init__(self, value): def __init__(self, value):

View File

@ -589,4 +589,4 @@ def report_status():
if OPTS.trim_netlist: if OPTS.trim_netlist:
debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).") debug.print_raw("Trimming netlist to speed up characterization (trim_netlist=False to disable).")
if OPTS.nominal_corner_only: if OPTS.nominal_corner_only:
debug.print_raw("Only characterizing nominal corner.") debug.print_raw("Only characterizing nominal corner.")

View File

@ -45,11 +45,9 @@ class bank_select(design.design):
self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width self.height = max([x.uy() for x in self.inv_inst]) + self.m1_width
self.width = max([x.rx() for x in self.inv_inst]) self.width = max([x.rx() for x in self.inv_inst])
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
# Number of control lines in the bus # Number of control lines in the bus
@ -65,19 +63,18 @@ class bank_select(design.design):
if (self.port == "rw") or (self.port == "r"): if (self.port == "rw") or (self.port == "r"):
self.input_control_signals.append("s_en") self.input_control_signals.append("s_en")
# These will be outputs of the gaters if this is multibank # These will be outputs of the gaters if this is multibank
self.control_signals = ["gated_"+str for str in self.input_control_signals] self.control_signals = ["gated_" + str for str in self.input_control_signals]
self.add_pin_list(self.input_control_signals, "INPUT") self.add_pin_list(self.input_control_signals, "INPUT")
self.add_pin("bank_sel") self.add_pin("bank_sel")
self.add_pin_list(self.control_signals, "OUTPUT") self.add_pin_list(self.control_signals, "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):
""" Create modules for later instantiation """ """ Create modules for later instantiation """
self.bitcell = factory.create(module_type="bitcell") self.dff = factory.create(module_type="dff")
height = self.dff.height + drc("poly_to_active")
height = self.bitcell.height + drc("poly_to_active")
# 1x Inverter # 1x Inverter
self.inv_sel = factory.create(module_type="pinv", height=height) self.inv_sel = factory.create(module_type="pinv", height=height)
@ -98,17 +95,15 @@ class bank_select(design.design):
def calculate_module_offsets(self): def calculate_module_offsets(self):
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nand = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc("pwell_to_nwell") self.xoffset_nor = self.inv4x.width + 3 * self.m2_pitch + drc("pwell_to_nwell")
self.xoffset_bank_sel_inv = 0 self.xoffset_bank_sel_inv = 0
self.xoffset_inputs = 0 self.xoffset_inputs = 0
self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height self.yoffset_maxpoint = self.num_control_lines * self.inv4x.height
def create_instances(self): def create_instances(self):
self.bank_sel_inv=self.add_inst(name="bank_sel_inv", self.bank_sel_inv=self.add_inst(name="bank_sel_inv",
mod=self.inv_sel) mod=self.inv_sel)
self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"]) self.connect_inst(["bank_sel", "bank_sel_bar", "vdd", "gnd"])
@ -125,36 +120,36 @@ class bank_select(design.design):
# (writes occur on clk low) # (writes occur on clk low)
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
self.logic_inst.append(self.add_inst(name=name_nor, self.logic_inst.append(self.add_inst(name=name_nor,
mod=self.nor2)) mod=self.nor2))
self.connect_inst([input_name, self.connect_inst([input_name,
"bank_sel_bar", "bank_sel_bar",
gated_name+"_temp_bar", gated_name + "_temp_bar",
"vdd", "vdd",
"gnd"]) "gnd"])
# They all get inverters on the output # They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv, self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x_nor)) mod=self.inv4x_nor))
self.connect_inst([gated_name+"_temp_bar", self.connect_inst([gated_name + "_temp_bar",
gated_name, gated_name,
"vdd", "vdd",
"gnd"]) "gnd"])
# the rest are AND (nand2+inv) gates # the rest are AND (nand2+inv) gates
else: else:
self.logic_inst.append(self.add_inst(name=name_nand, self.logic_inst.append(self.add_inst(name=name_nand,
mod=self.nand2)) mod=self.nand2))
self.connect_inst([input_name, self.connect_inst([input_name,
"bank_sel", "bank_sel",
gated_name+"_temp_bar", gated_name + "_temp_bar",
"vdd", "vdd",
"gnd"]) "gnd"])
# They all get inverters on the output # They all get inverters on the output
self.inv_inst.append(self.add_inst(name=name_inv, self.inv_inst.append(self.add_inst(name=name_inv,
mod=self.inv4x)) mod=self.inv4x))
self.connect_inst([gated_name+"_temp_bar", self.connect_inst([gated_name + "_temp_bar",
gated_name, gated_name,
"vdd", "vdd",
"gnd"]) "gnd"])
@ -177,9 +172,9 @@ class bank_select(design.design):
if i == 0: if i == 0:
y_offset = 0 y_offset = 0
else: else:
y_offset = self.inv4x_nor.height + self.inv4x.height * (i-1) y_offset = self.inv4x_nor.height + self.inv4x.height * (i - 1)
if i%2: if i % 2:
y_offset += self.inv4x.height y_offset += self.inv4x.height
mirror = "MX" mirror = "MX"
else: else:
@ -200,7 +195,6 @@ class bank_select(design.design):
# They all get inverters on the output # They all get inverters on the output
inv_inst.place(offset=[logic_inst.rx(), y_offset], inv_inst.place(offset=[logic_inst.rx(), y_offset],
mirror=mirror) mirror=mirror)
def route_instances(self): def route_instances(self):
@ -222,57 +216,56 @@ class bank_select(design.design):
end=bank_sel_pin_end) end=bank_sel_pin_end)
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=bank_sel_pin_end, offset=bank_sel_pin_end,
directions=("H","H")) directions=("H", "H"))
# bank_sel_bar is vertical wire # bank_sel_bar is vertical wire
bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z") bank_sel_bar_pin = self.bank_sel_inv.get_pin("Z")
xoffset_bank_sel_bar = bank_sel_bar_pin.rx() xoffset_bank_sel_bar = bank_sel_bar_pin.rx()
self.add_label_pin(text="bank_sel_bar", self.add_label_pin(text="bank_sel_bar",
layer="m2", layer="m2",
offset=vector(xoffset_bank_sel_bar, 0), offset=vector(xoffset_bank_sel_bar, 0),
height=self.inv4x.height) height=self.inv4x.height)
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=bank_sel_bar_pin.rc()) offset=bank_sel_bar_pin.rc())
for i in range(self.num_control_lines): for i in range(self.num_control_lines):
logic_inst = self.logic_inst[i] logic_inst = self.logic_inst[i]
inv_inst = self.inv_inst[i] inv_inst = self.inv_inst[i]
input_name = self.input_control_signals[i] input_name = self.input_control_signals[i]
gated_name = self.control_signals[i] gated_name = self.control_signals[i]
if input_name in ("clk_buf"): if input_name in ("clk_buf"):
xoffset_bank_signal = xoffset_bank_sel_bar xoffset_bank_signal = xoffset_bank_sel_bar
else: else:
xoffset_bank_signal = xoffset_bank_sel xoffset_bank_signal = xoffset_bank_sel
# Connect the logic output to inverter input # Connect the logic output to inverter input
pre = logic_inst.get_pin("Z").lc() out_pin = logic_inst.get_pin("Z")
out_position = logic_inst.get_pin("Z").rc() + vector(0.5*self.m1_width,0) out_pos = out_pin.rc()
in_position = inv_inst.get_pin("A").lc() + vector(0.5*self.m1_width,0) in_pin = inv_inst.get_pin("A")
post = inv_inst.get_pin("A").rc() in_pos = in_pin.lc()
self.add_path("m1", [pre, out_position, in_position, post]) mid1_pos = vector(0.5 * (out_pos.x + in_pos.x), out_pos.y)
mid2_pos = vector(0.5 * (out_pos.x + in_pos.x), in_pos.y)
self.add_path("m1", [out_pos, mid1_pos, mid2_pos, in_pos])
# Connect the logic B input to bank_sel / bank_sel_bar
# 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_pos = logic_inst.get_pin("B").lc() - vector(0.5*contact.m1_via.height,0)
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("m2", [logic_pos, input_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) directions=("H", "H"))
# 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_pos = logic_inst.get_pin("A").lc()
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_center(layers=self.m1_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) directions=("H", "H"))
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=logic_pos, offset=logic_pos,
directions=("H","H")) 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,
@ -286,7 +279,6 @@ class bank_select(design.design):
width=inv_inst.rx() - out_pin.lx(), width=inv_inst.rx() - out_pin.lx(),
height=out_pin.height()) height=out_pin.height())
# Find the x offsets for where the vias/pins should be placed # Find the x offsets for where the vias/pins should be placed
a_xoffset = self.logic_inst[0].lx() a_xoffset = self.logic_inst[0].lx()
b_xoffset = self.inv_inst[0].lx() b_xoffset = self.inv_inst[0].lx()
@ -294,7 +286,7 @@ class bank_select(design.design):
# Route both supplies # Route both supplies
for n in ["vdd", "gnd"]: for n in ["vdd", "gnd"]:
supply_pin = self.inv_inst[num].get_pin(n) supply_pin = self.inv_inst[num].get_pin(n)
supply_offset = supply_pin.ll().scale(0,1) supply_offset = supply_pin.ll().scale(0, 1)
self.add_rect(layer="m1", self.add_rect(layer="m1",
offset=supply_offset, offset=supply_offset,
width=self.width) width=self.width)
@ -304,10 +296,10 @@ class bank_select(design.design):
pin_pos = vector(xoffset, supply_pin.cy()) pin_pos = vector(xoffset, supply_pin.cy())
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin_pos, offset=pin_pos,
directions=("H","H")) directions=("H", "H"))
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=pin_pos, offset=pin_pos,
directions=("H","H")) directions=("H", "H"))
self.add_layout_pin_rect_center(text=n, self.add_layout_pin_rect_center(text=n,
layer="m3", layer="m3",
offset=pin_pos) offset=pin_pos)

View File

@ -5,18 +5,16 @@
# (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, parameter
from tech import cell_properties as props from tech import cell_properties as props
import debug import debug
import contact
from sram_factory import factory from sram_factory import factory
import math import math
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
import logical_effort import logical_effort
class control_logic(design.design): class control_logic(design.design):
""" """
Dynamically generated Control logic for the total SRAM circuit. Dynamically generated Control logic for the total SRAM circuit.
@ -29,7 +27,7 @@ class control_logic(design.design):
debug.info(1, "Creating {}".format(name)) debug.info(1, "Creating {}".format(name))
self.add_comment("num_rows: {0}".format(num_rows)) self.add_comment("num_rows: {0}".format(num_rows))
self.add_comment("words_per_row: {0}".format(words_per_row)) self.add_comment("words_per_row: {0}".format(words_per_row))
self.add_comment("word_size {0}".format(word_size)) self.add_comment("word_size {0}".format(word_size))
self.sram=sram self.sram=sram
self.num_rows = num_rows self.num_rows = num_rows
@ -37,14 +35,15 @@ class control_logic(design.design):
self.word_size = word_size self.word_size = word_size
self.port_type = port_type self.port_type = port_type
self.num_cols = word_size*words_per_row self.num_cols = word_size * words_per_row
self.num_words = num_rows*words_per_row self.num_words = num_rows * words_per_row
self.enable_delay_chain_resizing = False self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv self.inv_parasitic_delay = logical_effort.logical_effort.pinv
# Determines how much larger the sen delay should be. Accounts for possible error in model. # Determines how much larger the sen delay should be. Accounts for possible error in model.
self.wl_timing_tolerance = 1 # FIXME: This should be made a parameter
self.wl_timing_tolerance = 1
self.wl_stage_efforts = None self.wl_stage_efforts = None
self.sen_stage_efforts = None self.sen_stage_efforts = None
@ -67,17 +66,16 @@ class control_logic(design.design):
""" Create layout and route between modules """ """ Create layout and route between modules """
self.place_instances() self.place_instances()
self.route_all() self.route_all()
#self.add_lvs_correspondence_points() # self.add_lvs_correspondence_points()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def add_pins(self): def add_pins(self):
""" Add the pins to the control logic module. """ """ Add the pins to the control logic module. """
self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT") self.add_pin_list(self.input_list + ["clk"] + self.rbl_list, "INPUT")
self.add_pin_list(self.output_list,"OUTPUT") self.add_pin_list(self.output_list, "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):
""" Add all the required modules """ """ Add all the required modules """
@ -101,14 +99,13 @@ class control_logic(design.design):
height=dff_height) height=dff_height)
self.add_mod(self.rbl_driver) self.add_mod(self.rbl_driver)
# clk_buf drives a flop for every address
# clk_buf drives a flop for every address addr_flops = math.log(self.num_words, 2) + math.log(self.words_per_row, 2)
addr_flops = math.log(self.num_words,2) + math.log(self.words_per_row,2)
# plus data flops and control flops # plus data flops and control flops
num_flops = addr_flops + self.word_size + self.num_control_signals num_flops = addr_flops + self.word_size + self.num_control_signals
# each flop internally has a FO 5 approximately # each flop internally has a FO 5 approximately
# plus about 5 fanouts for the control logic # plus about 5 fanouts for the control logic
clock_fanout = 5*num_flops + 5 clock_fanout = 5 * num_flops + 5
self.clk_buf_driver = factory.create(module_type="pdriver", self.clk_buf_driver = factory.create(module_type="pdriver",
fanout=clock_fanout, fanout=clock_fanout,
height=dff_height) height=dff_height)
@ -117,7 +114,7 @@ class control_logic(design.design):
# We will use the maximum since this same value is used to size the wl_en # We will use the maximum since this same value is used to size the wl_en
# and the p_en_bar drivers # and the p_en_bar drivers
max_fanout = max(self.num_rows,self.num_cols) max_fanout = max(self.num_rows, self.num_cols)
# wl_en drives every row in the bank # wl_en drives every row in the bank
self.wl_en_driver = factory.create(module_type="pdriver", self.wl_en_driver = factory.create(module_type="pdriver",
@ -127,7 +124,7 @@ class control_logic(design.design):
# w_en drives every write driver # w_en drives every write driver
self.wen_and = factory.create(module_type="pand3", self.wen_and = factory.create(module_type="pand3",
size=self.word_size+8, size=self.word_size + 8,
height=dff_height) height=dff_height)
self.add_mod(self.wen_and) self.add_mod(self.wen_and)
@ -137,7 +134,7 @@ class control_logic(design.design):
height=dff_height) height=dff_height)
self.add_mod(self.sen_and3) self.add_mod(self.sen_and3)
# used to generate inverted signals with low fanout # used to generate inverted signals with low fanout
self.inv = factory.create(module_type="pinv", self.inv = factory.create(module_type="pinv",
size=1, size=1,
height=dff_height) height=dff_height)
@ -151,7 +148,6 @@ class control_logic(design.design):
height=dff_height) height=dff_height)
self.add_mod(self.p_en_bar_driver) self.add_mod(self.p_en_bar_driver)
self.nand2 = factory.create(module_type="pnand2", self.nand2 = factory.create(module_type="pnand2",
height=dff_height) height=dff_height)
self.add_mod(self.nand2) self.add_mod(self.nand2)
@ -179,14 +175,14 @@ class control_logic(design.design):
# delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic, # delay_fanout_list=[delay_fanout_heuristic]*delay_stages_heuristic,
# bitcell_loads=bitcell_loads) # bitcell_loads=bitcell_loads)
# #Resize if necessary, condition depends on resizing method # #Resize if necessary, condition depends on resizing method
# if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match(): # if self.sram != None and self.enable_delay_chain_resizing and not self.does_sen_rise_fall_timing_match():
# #This resizes to match fall and rise delays, can make the delay chain weird sizes. # #This resizes to match fall and rise delays, can make the delay chain weird sizes.
# stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic) # stage_list = self.get_dynamic_delay_fanout_list(delay_stages_heuristic, delay_fanout_heuristic)
# self.replica_bitline = factory.create(module_type="replica_bitline", # self.replica_bitline = factory.create(module_type="replica_bitline",
# delay_fanout_list=stage_list, # delay_fanout_list=stage_list,
# bitcell_loads=bitcell_loads) # bitcell_loads=bitcell_loads)
# #This resizes based on total delay. # #This resizes based on total delay.
# # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic) # # delay_stages, delay_fanout = self.get_dynamic_delay_chain_size(delay_stages_heuristic, delay_fanout_heuristic)
# # self.replica_bitline = factory.create(module_type="replica_bitline", # # self.replica_bitline = factory.create(module_type="replica_bitline",
# # delay_fanout_list=[delay_fanout]*delay_stages, # # delay_fanout_list=[delay_fanout]*delay_stages,
@ -195,9 +191,10 @@ class control_logic(design.design):
# self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing # self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() #get the new timing
# self.delay_chain_resized = True # self.delay_chain_resized = True
debug.check(OPTS.delay_chain_stages%2, "Must use odd number of delay chain stages for inverting delay chain.") debug.check(OPTS.delay_chain_stages % 2,
"Must use odd number of delay chain stages for inverting delay chain.")
self.delay_chain=factory.create(module_type="delay_chain", self.delay_chain=factory.create(module_type="delay_chain",
fanout_list = OPTS.delay_chain_stages*[OPTS.delay_chain_fanout_per_stage]) fanout_list = OPTS.delay_chain_stages * [ OPTS.delay_chain_fanout_per_stage ])
self.add_mod(self.delay_chain) self.add_mod(self.delay_chain)
def get_heuristic_delay_chain_size(self): def get_heuristic_delay_chain_size(self):
@ -219,17 +216,17 @@ class control_logic(design.design):
def set_sen_wl_delays(self): def set_sen_wl_delays(self):
"""Set delays for wordline and sense amp enable""" """Set delays for wordline and sense amp enable"""
self.wl_delay_rise,self.wl_delay_fall = self.get_delays_to_wl() self.wl_delay_rise, self.wl_delay_fall = self.get_delays_to_wl()
self.sen_delay_rise,self.sen_delay_fall = self.get_delays_to_sen() self.sen_delay_rise, self.sen_delay_fall = self.get_delays_to_sen()
self.wl_delay = self.wl_delay_rise+self.wl_delay_fall self.wl_delay = self.wl_delay_rise + self.wl_delay_fall
self.sen_delay = self.sen_delay_rise+self.sen_delay_fall self.sen_delay = self.sen_delay_rise + self.sen_delay_fall
def does_sen_rise_fall_timing_match(self): def does_sen_rise_fall_timing_match(self):
"""Compare the relative rise/fall delays of the sense amp enable and wordline""" """Compare the relative rise/fall delays of the sense amp enable and wordline"""
self.set_sen_wl_delays() self.set_sen_wl_delays()
# This is not necessarily more reliable than total delay in some cases. # This is not necessarily more reliable than total delay in some cases.
if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or if (self.wl_delay_rise * self.wl_timing_tolerance >= self.sen_delay_rise or
self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall): self.wl_delay_fall * self.wl_timing_tolerance >= self.sen_delay_fall):
return False return False
else: else:
return True return True
@ -240,91 +237,107 @@ class control_logic(design.design):
# The sen delay must always be bigger than than the wl # The sen delay must always be bigger than than the wl
# delay. This decides how much larger the sen delay must be # delay. This decides how much larger the sen delay must be
# before a re-size is warranted. # before a re-size is warranted.
if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay: if self.wl_delay * self.wl_timing_tolerance >= self.sen_delay:
return False return False
else: else:
return True return True
def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout): def get_dynamic_delay_chain_size(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays""" """Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
from math import ceil from math import ceil
previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages previous_delay_chain_delay = (previous_fanout + 1 + self.inv_parasitic_delay) * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
delay_fanout = 3 # This can be anything >=2 # This can be anything >=2
delay_fanout = 3
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) required_delay = self.wl_delay * self.wl_timing_tolerance - (self.sen_delay - previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay)) delay_per_stage = delay_fanout + 1 + self.inv_parasitic_delay
if delay_stages%2 == 1: #force an even number of stages. delay_stages = ceil(required_delay / delay_per_stage)
delay_stages+=1 # force an even number of stages.
if delay_stages % 2 == 1:
delay_stages += 1
# Fanout can be varied as well but is a little more complicated but potentially optimal. # Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout) return (delay_stages, delay_fanout)
def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout): def get_dynamic_delay_fanout_list(self, previous_stages, previous_fanout):
"""Determine the size of the delay chain used for the Sense Amp Enable using path delays""" """Determine the size of the delay chain used for the Sense Amp Enable using path delays"""
previous_delay_chain_delay = (previous_fanout+1+self.inv_parasitic_delay)*previous_stages previous_delay_per_stage = previous_fanout + 1 + self.inv_parasitic_delay
previous_delay_chain_delay = previous_delay_per_stage * previous_stages
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2 fanout_rise = fanout_fall = 2 # This can be anything >=2
# The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
# inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2) required_delay_fall = self.wl_delay_fall * self.wl_timing_tolerance - \
required_delay_rise = self.wl_delay_rise*self.wl_timing_tolerance - (self.sen_delay_rise-previous_delay_chain_delay/2) (self.sen_delay_fall - previous_delay_chain_delay / 2)
debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise)) required_delay_rise = self.wl_delay_rise * self.wl_timing_tolerance - \
(self.sen_delay_rise - previous_delay_chain_delay / 2)
debug.info(2,
"Required delays from chain: fall={}, rise={}".format(required_delay_fall,
required_delay_rise))
# If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5 WARNING_FANOUT_DIFF = 5
stages_close = False stages_close = False
# The stages need to be equal (or at least a even number of stages with matching rise/fall delays) # The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True: while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) fanout_fall)
debug.info(1,"Fall stages={}, rise stages={}".format(stages_fall,stages_rise)) stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,
if abs(stages_fall-stages_rise) == 1 and not stages_close: fanout_rise)
debug.info(1,
"Fall stages={}, rise stages={}".format(stages_fall,
stages_rise))
if abs(stages_fall - stages_rise) == 1 and not stages_close:
stages_close = True stages_close = True
safe_fanout_rise = fanout_rise safe_fanout_rise = fanout_rise
safe_fanout_fall = fanout_fall safe_fanout_fall = fanout_fall
if stages_fall == stages_rise: if stages_fall == stages_rise:
break break
elif abs(stages_fall-stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall-fanout_rise): elif abs(stages_fall - stages_rise) == 1 and WARNING_FANOUT_DIFF < abs(fanout_fall - fanout_rise):
debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.") debug.info(1, "Delay chain fanouts between stages are large. Making chain size larger for safety.")
fanout_rise = safe_fanout_rise fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall fanout_fall = safe_fanout_fall
break break
# There should also be a condition to make sure the fanout does not get too large. # There should also be a condition to make sure the fanout does not get too large.
# Otherwise, increase the fanout of delay with the most stages, calculate new stages # Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise: elif stages_fall>stages_rise:
fanout_fall+=1 fanout_fall+=1
else: else:
fanout_rise+=1 fanout_rise+=1
total_stages = max(stages_fall,stages_rise)*2 total_stages = max(stages_fall, stages_rise) * 2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
# Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. # Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)] stage_list = [fanout_fall if i % 2==0 else fanout_rise for i in range(total_stages)]
return stage_list return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout): def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil from math import ceil
# Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay # Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0). # 3 is the minimum delay per stage (with pinv=0).
if required_delay <= 3 + self.inv_parasitic_delay:
return 1 return 1
delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay)) delay_per_stage = fanout + 1 + self.inv_parasitic_delay
delay_stages = ceil(required_delay / delay_per_stage)
return delay_stages return delay_stages
def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall): def calculate_stage_list(self, total_stages, fanout_rise, fanout_fall):
"""Produces a list of fanouts which determine the size of the delay chain. List length is the number of stages. """
Assumes the first stage is falling. Produces a list of fanouts which determine the size of the delay chain.
List length is the number of stages.
Assumes the first stage is falling.
""" """
stage_list = [] stage_list = []
for i in range(total_stages): for i in range(total_stages):
if i%2 == 0: if i % 2 == 0:
stage_list.append() stage_list.append()
def setup_signal_busses(self): def setup_signal_busses(self):
@ -351,7 +364,7 @@ class control_logic(design.design):
else: else:
self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"] self.internal_bus_list = ["rbl_bl_delay_bar", "rbl_bl_delay", "gated_clk_bar", "gated_clk_buf", "clk_buf", "cs"]
# leave space for the bus plus one extra space # leave space for the bus plus one extra space
self.internal_bus_width = (len(self.internal_bus_list)+1)*self.m2_pitch self.internal_bus_width = (len(self.internal_bus_list) + 1) * self.m2_pitch
# Outputs to the bank # Outputs to the bank
if self.port_type == "rw": if self.port_type == "rw":
@ -366,15 +379,13 @@ class control_logic(design.design):
self.supply_list = ["vdd", "gnd"] self.supply_list = ["vdd", "gnd"]
def route_rails(self): def route_rails(self):
""" Add the input signal inverted tracks """ """ Add the input signal inverted tracks """
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", self.m2_pitch, offset, self.internal_bus_list, height)
def create_instances(self): def create_instances(self):
""" Create all the instances """ """ Create all the instances """
self.create_dffs() self.create_dffs()
@ -388,9 +399,7 @@ class control_logic(design.design):
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.create_sen_row() self.create_sen_row()
self.create_delay() self.create_delay()
self.create_pen_row() self.create_pen_row()
def place_instances(self): def place_instances(self):
""" Place all the instances """ """ Place all the instances """
@ -406,13 +415,13 @@ class control_logic(design.design):
row = 0 row = 0
# Add the logic on the right of the bus # Add the logic on the right of the bus
self.place_clk_buf_row(row) self.place_clk_buf_row(row)
row += 1 row += 1
self.place_gated_clk_bar_row(row) self.place_gated_clk_bar_row(row)
row += 1 row += 1
self.place_gated_clk_buf_row(row) self.place_gated_clk_buf_row(row)
row += 1 row += 1
self.place_wlen_row(row) self.place_wlen_row(row)
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_wen_row(row) self.place_wen_row(row)
@ -421,10 +430,10 @@ class control_logic(design.design):
row += 1 row += 1
self.place_pen_row(row) self.place_pen_row(row)
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row) self.place_rbl_delay_row(row)
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row) self.place_sen_row(row)
row += 1 row += 1
self.place_delay(row) self.place_delay(row)
@ -435,11 +444,11 @@ class control_logic(design.design):
self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y) self.control_logic_center = vector(self.ctrl_dff_inst.rx(), control_center_y)
# Extra pitch on top and right # Extra pitch on top and right
self.height = height + 2*self.m1_pitch self.height = height + 2 * self.m1_pitch
# Max of modules or logic rows # Max of modules or logic rows
self.width = max([inst.rx() for inst in self.row_end_inst]) self.width = max([inst.rx() for inst in self.row_end_inst])
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.width = max(self.delay_inst.rx() , self.width) self.width = max(self.delay_inst.rx(), self.width)
self.width += self.m2_pitch self.width += self.m2_pitch
def route_all(self): def route_all(self):
@ -459,7 +468,6 @@ class control_logic(design.design):
self.route_gated_clk_buf() self.route_gated_clk_buf()
self.route_supply() self.route_supply()
def create_delay(self): def create_delay(self):
""" Create the replica bitline """ """ Create the replica bitline """
self.delay_inst=self.add_inst(name="delay_chain", self.delay_inst=self.add_inst(name="delay_chain",
@ -467,9 +475,9 @@ class control_logic(design.design):
# rbl_bl_delay is asserted (1) when the bitline has been discharged # rbl_bl_delay is asserted (1) when the bitline has been discharged
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"]) self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self,row): def place_delay(self, row):
""" Place the replica bitline """ """ Place the replica bitline """
y_off = row * self.and2.height + 2*self.m1_pitch y_off = row * self.and2.height + 2 * self.m1_pitch
# Add the RBL above the rows # Add the RBL above the rows
# Add to the right of the control rows and routing channel # Add to the right of the control rows and routing channel
@ -482,24 +490,22 @@ class control_logic(design.design):
# Connect to the rail level with the vdd rail # Connect to the rail level with the vdd rail
# Use pen since it is in every type of control logic # Use pen since it is in every type of control logic
vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by() vdd_ypos = self.p_en_bar_nand_inst.get_pin("vdd").by()
in_pos = vector(self.rail_offsets["rbl_bl_delay"].x,vdd_ypos) in_pos = vector(self.rail_offsets["rbl_bl_delay"].x, vdd_ypos)
mid1 = vector(out_pos.x,in_pos.y) mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack,[out_pos, mid1, in_pos]) self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=in_pos) offset=in_pos)
# Input from RBL goes to the delay line for futher delay # Input from RBL goes to the delay line for futher delay
self.copy_layout_pin(self.delay_inst, "in", "rbl_bl") self.copy_layout_pin(self.delay_inst, "in", "rbl_bl")
def create_clk_buf_row(self): def create_clk_buf_row(self):
""" Create the multistage and gated clock buffer """ """ Create the multistage and gated clock buffer """
self.clk_buf_inst = self.add_inst(name="clkbuf", self.clk_buf_inst = self.add_inst(name="clkbuf",
mod=self.clk_buf_driver) mod=self.clk_buf_driver)
self.connect_inst(["clk","clk_buf","vdd","gnd"]) self.connect_inst(["clk", "clk_buf", "vdd", "gnd"])
def place_clk_buf_row(self,row): def place_clk_buf_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.clk_buf_inst, x_offset, row) x_offset = self.place_util(self.clk_buf_inst, x_offset, row)
@ -512,17 +518,16 @@ class control_logic(design.design):
self.add_layout_pin_segment_center(text="clk", self.add_layout_pin_segment_center(text="clk",
layer="m2", layer="m2",
start=clk_pos, start=clk_pos,
end=clk_pos.scale(1,0)) end=clk_pos.scale(1, 0))
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 # Connect this at the bottom of the buffer
out_pos = self.clk_buf_inst.get_pin("Z").center() out_pos = self.clk_buf_inst.get_pin("Z").center()
mid1 = vector(out_pos.x,2*self.m2_pitch) mid1 = vector(out_pos.x, 2 * self.m2_pitch)
mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y) mid2 = vector(self.rail_offsets["clk_buf"].x, mid1.y)
bus_pos = self.rail_offsets["clk_buf"] bus_pos = self.rail_offsets["clk_buf"]
self.add_wire(("m3","via2","m2"),[out_pos, mid1, mid2, bus_pos]) 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 # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.clk_buf_inst.get_pin("Z").center()) offset=self.clk_buf_inst.get_pin("Z").center())
@ -532,40 +537,45 @@ class control_logic(design.design):
def create_gated_clk_bar_row(self): def create_gated_clk_bar_row(self):
self.clk_bar_inst = self.add_inst(name="inv_clk_bar", self.clk_bar_inst = self.add_inst(name="inv_clk_bar",
mod=self.inv) mod=self.inv)
self.connect_inst(["clk_buf","clk_bar","vdd","gnd"]) self.connect_inst(["clk_buf", "clk_bar", "vdd", "gnd"])
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(["cs", "clk_bar", "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
x_offset = self.place_util(self.clk_bar_inst, x_offset, row) x_offset = self.place_util(self.clk_bar_inst, x_offset, row)
x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row) x_offset = self.place_util(self.gated_clk_bar_inst, x_offset, row)
self.row_end_inst.append(self.gated_clk_bar_inst) self.row_end_inst.append(self.gated_clk_bar_inst)
def route_gated_clk_bar(self): def route_gated_clk_bar(self):
clkbuf_map = zip(["A"], ["clk_buf"]) clkbuf_map = zip(["A"], ["clk_buf"])
self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets) self.connect_vertical_bus(clkbuf_map, self.clk_bar_inst, self.rail_offsets)
out_pos = self.clk_bar_inst.get_pin("Z").center() out_pos = self.clk_bar_inst.get_pin("Z").center()
in_pos = self.gated_clk_bar_inst.get_pin("B").center() in_pos = self.gated_clk_bar_inst.get_pin("B").center()
mid1 = vector(in_pos.x,out_pos.y) mid1 = vector(in_pos.x, out_pos.y)
self.add_path("m1",[out_pos, mid1, 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(["A"], ["cs"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("A").center()) offset=self.gated_clk_bar_inst.get_pin("A").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"]) clkbuf_map = zip(["Z"], ["gated_clk_bar"])
self.connect_vertical_bus(clkbuf_map, self.gated_clk_bar_inst, self.rail_offsets, ("m3", "via2", "m2")) self.connect_vertical_bus(clkbuf_map,
self.gated_clk_bar_inst,
self.rail_offsets,
self.m2_stack[::-1])
# The pin is on M1, so we need another via as well # The pin is on M1, so we need another via as well
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_bar_inst.get_pin("Z").center()) offset=self.gated_clk_bar_inst.get_pin("Z").center())
@ -573,9 +583,9 @@ class control_logic(design.design):
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",
mod=self.and2) mod=self.and2)
self.connect_inst(["clk_buf", "cs","gated_clk_buf","vdd","gnd"]) self.connect_inst(["clk_buf", "cs", "gated_clk_buf", "vdd", "gnd"])
def place_gated_clk_buf_row(self,row): def place_gated_clk_buf_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row) x_offset = self.place_util(self.gated_clk_buf_inst, x_offset, row)
@ -584,11 +594,13 @@ 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.gated_clk_buf_inst, self.rail_offsets, ("m3", "via2", "m2")) self.connect_vertical_bus(clkbuf_map,
self.gated_clk_buf_inst,
self.rail_offsets,
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, self.add_via_center(layers=self.m1_stack,
offset=self.gated_clk_buf_inst.get_pin("Z").center()) offset=self.gated_clk_buf_inst.get_pin("Z").center())
@ -602,7 +614,7 @@ class control_logic(design.design):
def place_wlen_row(self, row): def place_wlen_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.wl_en_inst, x_offset, row) x_offset = self.place_util(self.wl_en_inst, x_offset, row)
self.row_end_inst.append(self.wl_en_inst) self.row_end_inst.append(self.wl_en_inst)
@ -623,11 +635,11 @@ class control_logic(design.design):
mod=self.p_en_bar_driver) mod=self.p_en_bar_driver)
self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"]) self.connect_inst(["p_en_bar_unbuf", "p_en_bar", "vdd", "gnd"])
def place_pen_row(self,row): def place_pen_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row) x_offset = self.place_util(self.p_en_bar_nand_inst, x_offset, row)
x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row) x_offset = self.place_util(self.p_en_bar_driver_inst, x_offset, row)
self.row_end_inst.append(self.p_en_bar_driver_inst) self.row_end_inst.append(self.p_en_bar_driver_inst)
@ -637,8 +649,8 @@ class control_logic(design.design):
out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc() out_pos = self.p_en_bar_nand_inst.get_pin("Z").rc()
in_pos = self.p_en_bar_driver_inst.get_pin("A").lc() in_pos = self.p_en_bar_driver_inst.get_pin("A").lc()
mid1 = vector(out_pos.x,in_pos.y) mid1 = vector(out_pos.x, in_pos.y)
self.add_wire(self.m1_stack,[out_pos, mid1,in_pos]) self.add_wire(self.m1_stack, [out_pos, mid1, in_pos])
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")
@ -656,14 +668,12 @@ class control_logic(design.design):
# hence we use rbl_bl_delay as well. # hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"]) self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
def place_sen_row(self, row):
def place_sen_row(self,row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.s_en_gate_inst, x_offset, row) x_offset = self.place_util(self.s_en_gate_inst, x_offset, row)
self.row_end_inst.append(self.s_en_gate_inst) self.row_end_inst.append(self.s_en_gate_inst)
def route_sen(self): def route_sen(self):
@ -683,7 +693,7 @@ class control_logic(design.design):
mod=self.inv) mod=self.inv)
self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"]) self.connect_inst(["rbl_bl_delay", "rbl_bl_delay_bar", "vdd", "gnd"])
def place_rbl_delay_row(self,row): def place_rbl_delay_row(self, row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row) x_offset = self.place_util(self.rbl_bl_delay_inv_inst, x_offset, row)
@ -700,11 +710,9 @@ class control_logic(design.design):
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=self.rbl_bl_delay_inv_inst.get_pin("Z").center()) 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)
def create_wen_row(self): def create_wen_row(self):
# input: we (or cs) output: w_en # input: we (or cs) output: w_en
@ -720,8 +728,7 @@ class control_logic(design.design):
# Only drive the writes in the second half of the clock cycle during a write operation. # Only drive the writes in the second half of the clock cycle during a write operation.
self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"]) self.connect_inst([input_name, "rbl_bl_delay_bar", "gated_clk_bar", "w_en", "vdd", "gnd"])
def place_wen_row(self, row):
def place_wen_row(self,row):
x_offset = self.control_x_offset x_offset = self.control_x_offset
x_offset = self.place_util(self.w_en_gate_inst, x_offset, row) x_offset = self.place_util(self.w_en_gate_inst, x_offset, row)
@ -750,22 +757,22 @@ class control_logic(design.design):
self.connect_inst(inst_pins) self.connect_inst(inst_pins)
def place_dffs(self): def place_dffs(self):
self.ctrl_dff_inst.place(vector(0,0)) self.ctrl_dff_inst.place(vector(0, 0))
def route_dffs(self): def route_dffs(self):
if self.port_type == "rw": if self.port_type == "rw":
dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"]) dff_out_map = zip(["dout_bar_0", "dout_bar_1", "dout_1"], ["cs", "we", "we_bar"])
elif self.port_type == "r": elif self.port_type == "r":
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, ("m3", "via2", "m2"))
# Connect the clock rail to the other clock rail # Connect the clock rail to the other clock rail
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, 2 * self.m2_pitch)
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,
offset=rail_pos) offset=rail_pos)
@ -773,34 +780,31 @@ class control_logic(design.design):
if (self.port_type == "rw"): if (self.port_type == "rw"):
self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web") self.copy_layout_pin(self.ctrl_dff_inst, "din_1", "web")
def get_offset(self,row): def get_offset(self, row):
""" Compute the y-offset and mirroring """ """ Compute the y-offset and mirroring """
y_off = row*self.and2.height y_off = row * self.and2.height
if row % 2: if row % 2:
y_off += self.and2.height y_off += self.and2.height
mirror="MX" mirror="MX"
else: else:
mirror="R0" mirror="R0"
return (y_off,mirror) return (y_off, mirror)
def connect_output(self, inst, pin_name, out_name): def connect_output(self, inst, pin_name, out_name):
""" Create an output pin on the right side from the pin of a given instance. """ """ Create an output pin on the right side from the pin of a given instance. """
out_pin = inst.get_pin(pin_name) out_pin = inst.get_pin(pin_name)
right_pos=out_pin.center() + vector(self.width-out_pin.cx(),0) right_pos = out_pin.center() + vector(self.width - out_pin.cx(), 0)
self.add_layout_pin_segment_center(text=out_name, self.add_layout_pin_segment_center(text=out_name,
layer="m1", layer="m1",
start=out_pin.center(), start=out_pin.center(),
end=right_pos) end=right_pos)
def route_supply(self): def route_supply(self):
""" Add vdd and gnd to the instance cells """ """ Add vdd and gnd to the instance cells """
max_row_x_loc = max([inst.rx() for inst in self.row_end_inst]) max_row_x_loc = max([inst.rx() for inst in self.row_end_inst])
for inst in self.row_end_inst: for inst in self.row_end_inst:
pins = inst.get_pins("vdd") pins = inst.get_pins("vdd")
for pin in pins: for pin in pins:
@ -818,16 +822,14 @@ class control_logic(design.design):
self.add_power_pin("gnd", pin_loc) self.add_power_pin("gnd", pin_loc)
self.add_path("m1", [row_loc, pin_loc]) self.add_path("m1", [row_loc, pin_loc])
self.copy_layout_pin(self.delay_inst,"gnd") self.copy_layout_pin(self.delay_inst, "gnd")
self.copy_layout_pin(self.delay_inst,"vdd") self.copy_layout_pin(self.delay_inst, "vdd")
self.copy_layout_pin(self.ctrl_dff_inst,"gnd") self.copy_layout_pin(self.ctrl_dff_inst, "gnd")
self.copy_layout_pin(self.ctrl_dff_inst,"vdd") self.copy_layout_pin(self.ctrl_dff_inst, "vdd")
def add_lvs_correspondence_points(self): def add_lvs_correspondence_points(self):
""" This adds some points for easier debugging if LVS goes wrong. """ This adds some points for easier debugging if LVS goes wrong.
These should probably be turned off by default though, since extraction These should probably be turned off by default though, since extraction
will show these as ports in the extracted netlist. will show these as ports in the extracted netlist.
""" """
@ -851,74 +853,79 @@ class control_logic(design.design):
offset=pin.ll(), offset=pin.ll(),
height=pin.height(), height=pin.height(),
width=pin.width()) width=pin.width())
def get_delays_to_wl(self): def get_delays_to_wl(self):
"""Get the delay (in delay units) of the clk to a wordline in the bitcell array""" """Get the delay (in delay units) of the clk to a wordline in the bitcell array"""
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.wl_stage_efforts = self.get_wordline_stage_efforts() self.wl_stage_efforts = self.get_wordline_stage_efforts()
clk_to_wl_rise,clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts) clk_to_wl_rise, clk_to_wl_fall = logical_effort.calculate_relative_rise_fall_delays(self.wl_stage_efforts)
total_delay = clk_to_wl_rise + clk_to_wl_fall total_delay = clk_to_wl_rise + clk_to_wl_fall
debug.info(1, "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise, clk_to_wl_fall,total_delay)) debug.info(1,
return clk_to_wl_rise,clk_to_wl_fall "Clock to wl delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_wl_rise,
clk_to_wl_fall,
total_delay))
return clk_to_wl_rise, clk_to_wl_fall
def get_wordline_stage_efforts(self): def get_wordline_stage_efforts(self):
"""Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts""" """Follows the gated_clk_bar -> wl_en -> wordline signal for the total path efforts"""
stage_effort_list = [] stage_effort_list = []
#Initial direction of gated_clk_bar signal for this path # Initial direction of gated_clk_bar signal for this path
is_clk_bar_rise = True is_clk_bar_rise = True
#Calculate the load on wl_en within the module and add it to external load # Calculate the load on wl_en within the module and add it to external load
external_cout = self.sram.get_wl_en_cin() external_cout = self.sram.get_wl_en_cin()
#First stage is the clock buffer # First stage is the clock buffer
stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise) stage_effort_list += self.clk_buf_driver.get_stage_efforts(external_cout, is_clk_bar_rise)
last_stage_is_rise = stage_effort_list[-1].is_rise last_stage_is_rise = stage_effort_list[-1].is_rise
#Then ask the sram for the other path delays (from the bank) # Then ask the sram for the other path delays (from the bank)
stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise) stage_effort_list += self.sram.get_wordline_stage_efforts(last_stage_is_rise)
return stage_effort_list return stage_effort_list
def get_delays_to_sen(self): def get_delays_to_sen(self):
"""Get the delay (in delay units) of the clk to a sense amp enable. """
This does not incorporate the delay of the replica bitline. Get the delay (in delay units) of the clk to a sense amp enable.
This does not incorporate the delay of the replica bitline.
""" """
debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.") debug.check(self.sram.all_mods_except_control_done, "Cannot calculate sense amp enable delay unless all module have been added.")
self.sen_stage_efforts = self.get_sa_enable_stage_efforts() self.sen_stage_efforts = self.get_sa_enable_stage_efforts()
clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts) clk_to_sen_rise, clk_to_sen_fall = logical_effort.calculate_relative_rise_fall_delays(self.sen_stage_efforts)
total_delay = clk_to_sen_rise + clk_to_sen_fall total_delay = clk_to_sen_rise + clk_to_sen_fall
debug.info(1, "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise, clk_to_sen_fall,total_delay)) debug.info(1,
return clk_to_sen_rise, clk_to_sen_fall "Clock to s_en delay is rise={:.3f}, fall={:.3f}, total={:.3f} in delay units".format(clk_to_sen_rise,
clk_to_sen_fall,
total_delay))
return clk_to_sen_rise, clk_to_sen_fall
def get_sa_enable_stage_efforts(self): def get_sa_enable_stage_efforts(self):
"""Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list""" """Follows the gated_clk_bar signal to the sense amp enable signal adding each stages stage effort to a list"""
stage_effort_list = [] stage_effort_list = []
#Initial direction of clock signal for this path # Initial direction of clock signal for this path
last_stage_rise = True last_stage_rise = True
#First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports. # First stage, gated_clk_bar -(and2)-> rbl_in. Only for RW ports.
if self.port_type == "rw": if self.port_type == "rw":
stage1_cout = self.replica_bitline.get_en_cin() stage1_cout = self.replica_bitline.get_en_cin()
stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise) stage_effort_list += self.and2.get_stage_efforts(stage1_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
#Replica bitline stage, rbl_in -(rbl)-> pre_s_en # Replica bitline stage, rbl_in -(rbl)-> pre_s_en
stage2_cout = self.sen_and2.get_cin() stage2_cout = self.sen_and2.get_cin()
stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise) stage_effort_list += self.replica_bitline.determine_sen_stage_efforts(stage2_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
#buffer stage, pre_s_en -(buffer)-> s_en # buffer stage, pre_s_en -(buffer)-> s_en
stage3_cout = self.sram.get_sen_cin() stage3_cout = self.sram.get_sen_cin()
stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise) stage_effort_list += self.s_en_driver.get_stage_efforts(stage3_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
return stage_effort_list return stage_effort_list
def get_wl_sen_delays(self): def get_wl_sen_delays(self):
"""Gets a list of the stages and delays in order of their path.""" """ Gets a list of the stages and delays in order of their path. """
if self.sen_stage_efforts == None or self.wl_stage_efforts == None: if self.sen_stage_efforts == None or self.wl_stage_efforts == None:
debug.error("Model delays not calculated for SRAM.", 1) debug.error("Model delays not calculated for SRAM.", 1)
@ -927,45 +934,45 @@ class control_logic(design.design):
return wl_delays, sen_delays return wl_delays, sen_delays
def analytical_delay(self, corner, slew, load): def analytical_delay(self, corner, slew, load):
"""Gets the analytical delay from clk input to wl_en output""" """ Gets the analytical delay from clk input to wl_en output """
stage_effort_list = [] stage_effort_list = []
#Calculate the load on clk_buf_bar # Calculate the load on clk_buf_bar
ext_clk_buf_cout = self.sram.get_clk_bar_cin() # ext_clk_buf_cout = self.sram.get_clk_bar_cin()
#Operations logic starts on negative edge # Operations logic starts on negative edge
last_stage_rise = False last_stage_rise = False
#First stage(s), clk -(pdriver)-> clk_buf. # First stage(s), clk -(pdriver)-> clk_buf.
#clk_buf_cout = self.replica_bitline.get_en_cin() # clk_buf_cout = self.replica_bitline.get_en_cin()
clk_buf_cout = 0 clk_buf_cout = 0
stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise) stage_effort_list += self.clk_buf_driver.get_stage_efforts(clk_buf_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
#Second stage, clk_buf -(inv)-> clk_bar # Second stage, clk_buf -(inv)-> clk_bar
clk_bar_cout = self.and2.get_cin() clk_bar_cout = self.and2.get_cin()
stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise) stage_effort_list += self.and2.get_stage_efforts(clk_bar_cout, last_stage_rise)
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
#Third stage clk_bar -(and)-> gated_clk_bar # Third stage clk_bar -(and)-> gated_clk_bar
gated_clk_bar_cin = self.get_gated_clk_bar_cin() gated_clk_bar_cin = self.get_gated_clk_bar_cin()
stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise)) stage_effort_list.append(self.inv.get_stage_effort(gated_clk_bar_cin, last_stage_rise))
last_stage_rise = stage_effort_list[-1].is_rise last_stage_rise = stage_effort_list[-1].is_rise
#Stages from gated_clk_bar -------> wordline # Stages from gated_clk_bar -------> wordline
stage_effort_list += self.get_wordline_stage_efforts() stage_effort_list += self.get_wordline_stage_efforts()
return stage_effort_list return stage_effort_list
def get_clk_buf_cin(self): def get_clk_buf_cin(self):
""" """
Get the loads that are connected to the buffered clock. Get the loads that are connected to the buffered clock.
Includes all the DFFs and some logic. Includes all the DFFs and some logic.
""" """
#Control logic internal load # Control logic internal load
int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin() int_clk_buf_cap = self.inv.get_cin() + self.ctrl_dff_array.get_clk_cin() + self.and2.get_cin()
#Control logic external load (in the other parts of the SRAM) # Control logic external load (in the other parts of the SRAM)
ext_clk_buf_cap = self.sram.get_clk_bar_cin() ext_clk_buf_cap = self.sram.get_clk_bar_cin()
return int_clk_buf_cap + ext_clk_buf_cap return int_clk_buf_cap + ext_clk_buf_cap
@ -976,7 +983,7 @@ class control_logic(design.design):
total_cin = 0 total_cin = 0
total_cin += self.wl_en_driver.get_cin() total_cin += self.wl_en_driver.get_cin()
if self.port_type == 'rw': if self.port_type == 'rw':
total_cin +=self.and2.get_cin() total_cin += self.and2.get_cin()
return total_cin return total_cin
def graph_exclude_dffs(self): def graph_exclude_dffs(self):
@ -989,7 +996,7 @@ class control_logic(design.design):
def place_util(self, inst, x_offset, row): def place_util(self, inst, x_offset, row):
""" Utility to place a row and compute the next offset """ """ Utility to place a row and compute the next offset """
(y_offset,mirror)=self.get_offset(row) (y_offset, mirror) = self.get_offset(row)
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

View File

@ -7,13 +7,13 @@
# #
import debug import debug
import design import design
from tech import drc,parameter from tech import parameter
from tech import cell_properties as props from tech import cell_properties as props
from math import log
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class dff_buf(design.design): class dff_buf(design.design):
""" """
This is a simple buffered DFF. The output is buffered This is a simple buffered DFF. The output is buffered
@ -107,13 +107,23 @@ class dff_buf(design.design):
self.dff_inst.place(vector(0,0)) self.dff_inst.place(vector(0,0))
# Add INV1 to the right # Add INV1 to the right
well_spacing = max(self.nwell_space, well_spacing = 0
self.pwell_space, try:
self.pwell_to_nwell) well_spacing = max(well_spacing, self.nwell_space)
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active,0)) except AttributeError:
pass
try:
well_spacing = max(well_spacing, self.pwell_space)
except AttributeError:
pass
try:
well_spacing = max(well_spacing, self.pwell_to_nwell)
except AttributeError:
pass
self.inv1_inst.place(vector(self.dff_inst.rx() + well_spacing + self.well_extend_active, 0))
# Add INV2 to the right # Add INV2 to the right
self.inv2_inst.place(vector(self.inv1_inst.rx(),0)) self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
def route_wires(self): def route_wires(self):
# Route dff q to inv1 a # Route dff q to inv1 a

View File

@ -11,13 +11,15 @@ import math
from sram_factory import factory 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 tech import cell_properties
class hierarchical_decoder(design.design): class hierarchical_decoder(design.design):
""" """
Dynamically generated hierarchical decoder. Dynamically generated hierarchical decoder.
""" """
def __init__(self, name, rows): def __init__(self, name, num_outputs):
design.design.__init__(self, name) design.design.__init__(self, name)
self.AND_FORMAT = "DEC_AND_{0}" self.AND_FORMAT = "DEC_AND_{0}"
@ -25,9 +27,16 @@ class hierarchical_decoder(design.design):
self.pre2x4_inst = [] self.pre2x4_inst = []
self.pre3x8_inst = [] self.pre3x8_inst = []
(self.cell_height, self.cell_multiple) = self.find_decoder_height() b = factory.create(module_type="bitcell")
self.rows = rows try:
self.num_inputs = math.ceil(math.log(self.rows, 2)) self.cell_multiple = cell_properties.bitcell.decoder_bitcell_multiple
except AttributeError:
self.cell_multiple = 1
# For debugging
self.cell_height = self.cell_multiple * b.height
self.num_outputs = num_outputs
self.num_inputs = math.ceil(math.log(self.num_outputs, 2))
(self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs) (self.no_of_pre2x4, self.no_of_pre3x8)=self.determine_predecodes(self.num_inputs)
self.create_netlist() self.create_netlist()
@ -35,20 +44,37 @@ class hierarchical_decoder(design.design):
self.create_layout() self.create_layout()
def find_decoder_height(self): def find_decoder_height(self):
"""
Dead code. This would dynamically determine the bitcell multiple,
but I just decided to hard code it in the tech file if it is not 1
because a DRC tool would be required even to run in front-end mode.
"""
b = factory.create(module_type="bitcell") b = factory.create(module_type="bitcell")
# Old behavior # Old behavior
return (b.height, 1) if OPTS.netlist_only:
return (b.height, 1)
# Search for the smallest multiple that works # Search for the smallest multiple that works
cell_multiple = 1 cell_multiple = 1
while cell_multiple < 3: while cell_multiple < 5:
cell_height = cell_multiple * b.height cell_height = cell_multiple * b.height
and3 = factory.create(module_type="pand3", # debug.info(2,"Trying mult = {0} height={1}".format(cell_multiple, cell_height))
height=cell_height) try:
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True) and3 = factory.create(module_type="pand3",
if drc_errors + lvs_errors == 0: height=cell_height)
return (cell_height, cell_multiple) except drc_error:
# debug.info(1, "Incrementing decoder height by 1 bitcell height {}".format(b.height))
pass
else:
(drc_errors, lvs_errors) = and3.DRC_LVS(force_check=True)
total_errors = drc_errors + lvs_errors
if total_errors == 0:
debug.info(1, "Decoder height is multiple of {} bitcells.".format(cell_multiple))
return (cell_height, cell_multiple)
cell_multiple += 1 cell_multiple += 1
else: else:
debug.error("Couldn't find a valid decoder height multiple.", -1) debug.error("Couldn't find a valid decoder height multiple.", -1)
@ -63,8 +89,8 @@ class hierarchical_decoder(design.design):
self.setup_layout_constants() self.setup_layout_constants()
self.place_pre_decoder() self.place_pre_decoder()
self.place_row_decoder() self.place_row_decoder()
self.route_input_rails() self.route_inputs()
self.route_predecode_rails() self.route_decoder_bus()
self.route_vdd_gnd() self.route_vdd_gnd()
self.offset_all_coordinates() self.offset_all_coordinates()
self.add_boundary() self.add_boundary()
@ -118,7 +144,7 @@ class hierarchical_decoder(design.design):
def setup_netlist_constants(self): def setup_netlist_constants(self):
self.predec_groups = [] # This array is a 2D array. self.predec_groups = [] # This array is a 2D array.
# Distributing vertical rails to different groups. One group belongs to one pre-decoder. # Distributing vertical bus to different groups. One group belongs to one pre-decoder.
# For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will # For example, for two 2:4 pre-decoder and one 3:8 pre-decoder, we will
# have total 16 output lines out of these 3 pre-decoders and they will # have total 16 output lines out of these 3 pre-decoders and they will
# be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ] # be distributed as [ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
@ -157,39 +183,46 @@ class hierarchical_decoder(design.design):
self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8 self.predecoder_height = self.pre2_4.height * self.no_of_pre2x4 + self.pre3_8.height * self.no_of_pre3x8
# We may have more than one bitcell per decoder row
self.num_rows = math.ceil(self.num_outputs / self.cell_multiple)
# We will place this many final decoders per row
self.decoders_per_row = math.ceil(self.num_outputs / self.num_rows)
# 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
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 self.internal_routing_width = self.m2_pitch * (self.total_number_of_predecoder_outputs + 1)
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.num_rows
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
self.height = self.row_decoder_height # Add extra pitch for good measure
self.height = max(self.predecoder_height, self.row_decoder_height) + self.m3_pitch
self.width = self.input_routing_width + self.predecoder_width \ self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.width + self.internal_routing_width \
+ self.decoders_per_row * nand_width + self.inv.width
def route_input_rails(self): def route_inputs(self):
""" Create input rails for the predecoders """ """ Create input bus for the predecoders """
# inputs should be as high as the decoders # inputs should be as high as the decoders
input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height input_height = self.no_of_pre2x4 * self.pre2_4.height + self.no_of_pre3x8 * self.pre3_8.height
# Find the left-most predecoder # Find the left-most predecoder
min_x = 0 min_x = 0
if self.no_of_pre2x4 > 0: if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width) min_x = min(min_x, self.pre2x4_inst[0].lx())
if self.no_of_pre3x8 > 0: if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width) min_x = min(min_x, self.pre3x8_inst[0].lx())
input_offset=vector(min_x - self.input_routing_width, 0) input_offset=vector(min_x - self.input_routing_width, 0)
input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)] input_bus_names = ["addr_{0}".format(i) for i in range(self.num_inputs)]
self.input_rails = self.create_vertical_pin_bus(layer="m2", self.input_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=input_offset, offset=input_offset,
names=input_bus_names, names=input_bus_names,
length=input_height) length=input_height)
self.route_input_to_predecodes() self.route_input_to_predecodes()
@ -199,7 +232,7 @@ class hierarchical_decoder(design.design):
for i in range(2): for i in range(2):
index = pre_num * 2 + i index = pre_num * 2 + i
input_pos = self.input_rails["addr_{}".format(index)] input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name) decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
@ -209,13 +242,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
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_rail(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
for i in range(3): for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2 index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pos = self.input_rails["addr_{}".format(index)] input_pos = self.input_bus["addr_{}".format(index)]
in_name = "in_{}".format(i) in_name = "in_{}".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name) decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
@ -225,10 +258,13 @@ class hierarchical_decoder(design.design):
decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height) decoder_offset = decoder_pin.bc() + vector(0, (i + 1) * self.inv.height)
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_rail(decoder_offset, input_offset) self.route_input_bus(decoder_offset, input_offset)
def route_input_rail(self, input_offset, output_offset): def route_input_bus(self, input_offset, output_offset):
""" Route a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """ """
Route a vertical M2 coordinate to another
vertical M2 coordinate to the predecode inputs
"""
self.add_via_center(layers=self.m2_stack, self.add_via_center(layers=self.m2_stack,
offset=input_offset) offset=input_offset)
@ -242,7 +278,7 @@ class hierarchical_decoder(design.design):
for i in range(self.num_inputs): for i in range(self.num_inputs):
self.add_pin("addr_{0}".format(i), "INPUT") self.add_pin("addr_{0}".format(i), "INPUT")
for j in range(self.rows): for j in range(self.num_outputs):
self.add_pin("decode_{0}".format(j), "OUTPUT") self.add_pin("decode_{0}".format(j), "OUTPUT")
self.add_pin("vdd", "POWER") self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND") self.add_pin("gnd", "GROUND")
@ -311,18 +347,17 @@ class hierarchical_decoder(design.design):
else: else:
base= vector(-self.pre2_4.width, num * self.pre2_4.height) base= vector(-self.pre2_4.width, num * self.pre2_4.height)
self.pre2x4_inst[num].place(base) self.pre2x4_inst[num].place(base - vector(2 * self.m2_pitch, 0))
def place_pre3x8(self, num): def place_pre3x8(self, num):
""" Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """ """ Place 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width, 0) offset = vector(-self.pre_3_8.width, 0)
mirror = "R0"
else: else:
height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height height = self.no_of_pre2x4 * self.pre2_4.height + num * self.pre3_8.height
offset = vector(-self.pre3_8.width, height) offset = vector(-self.pre3_8.width, height)
self.pre3x8_inst[num].place(offset) self.pre3x8_inst[num].place(offset - vector(2 * self.m2_pitch, 0))
def create_row_decoder(self): def create_row_decoder(self):
""" Create the row-decoder by placing AND2/AND3 and Inverters """ Create the row-decoder by placing AND2/AND3 and Inverters
@ -339,14 +374,14 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
row = len(self.predec_groups[0]) * j + i output = len(self.predec_groups[0]) * j + i
if (row < self.rows): if (output < self.num_outputs):
name = self.AND_FORMAT.format(row) name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.and2)) mod=self.and2))
pins =["out_{0}".format(i), pins =["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"decode_{0}".format(row), "decode_{0}".format(output),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
@ -355,18 +390,18 @@ class hierarchical_decoder(design.design):
for i in range(len(self.predec_groups[0])): for i in range(len(self.predec_groups[0])):
for j in range(len(self.predec_groups[1])): for j in range(len(self.predec_groups[1])):
for k in range(len(self.predec_groups[2])): for k in range(len(self.predec_groups[2])):
row = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \ output = (len(self.predec_groups[0]) * len(self.predec_groups[1])) * k \
+ len(self.predec_groups[0]) * j + i + len(self.predec_groups[0]) * j + i
if (row < self.rows): if (output < self.num_outputs):
name = self.AND_FORMAT.format(row) name = self.AND_FORMAT.format(output)
self.and_inst.append(self.add_inst(name=name, self.and_inst.append(self.add_inst(name=name,
mod=self.and3)) mod=self.and3))
pins = ["out_{0}".format(i), pins = ["out_{0}".format(i),
"out_{0}".format(j + len(self.predec_groups[0])), "out_{0}".format(j + len(self.predec_groups[0])),
"out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])), "out_{0}".format(k + len(self.predec_groups[0]) + len(self.predec_groups[1])),
"decode_{0}".format(row), "decode_{0}".format(output),
"vdd", "gnd"] "vdd", "gnd"]
self.connect_inst(pins) self.connect_inst(pins)
@ -380,7 +415,10 @@ class hierarchical_decoder(design.design):
self.route_decoder() self.route_decoder()
def place_decoder_and_array(self): def place_decoder_and_array(self):
""" Add a column of AND gates for final decode """ """
Add a column of AND gates for final decode.
This may have more than one decoder per row to match the bitcell height.
"""
# Row Decoder AND GATE array for address inputs <5. # Row Decoder AND GATE array for address inputs <5.
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
@ -392,9 +430,13 @@ class hierarchical_decoder(design.design):
self.place_and_array(and_mod=self.and3) self.place_and_array(and_mod=self.and3)
def place_and_array(self, and_mod): def place_and_array(self, and_mod):
""" Add a column of AND gates for the decoder above the predecoders.""" """
Add a column of AND gates for the decoder above the predecoders.
for row in range(self.rows): """
for inst_index in range(self.num_outputs):
row = math.floor(inst_index / self.decoders_per_row)
dec = inst_index % self.decoders_per_row
if ((row % 2) == 0): if ((row % 2) == 0):
y_off = and_mod.height * row y_off = and_mod.height * row
mirror = "R0" mirror = "R0"
@ -402,46 +444,52 @@ class hierarchical_decoder(design.design):
y_off = and_mod.height * (row + 1) y_off = and_mod.height * (row + 1)
mirror = "MX" mirror = "MX"
self.and_inst[row].place(offset=[self.internal_routing_width, y_off], x_off = self.internal_routing_width + dec * and_mod.width
mirror=mirror) self.and_inst[inst_index].place(offset=vector(x_off, y_off),
mirror=mirror)
def route_decoder(self): def route_decoder(self):
""" Add the pins. """ """ Add the pins. """
for row in range(self.rows): for output in range(self.num_outputs):
z_pin = self.and_inst[row].get_pin("Z") z_pin = self.and_inst[output].get_pin("Z")
self.add_layout_pin(text="decode_{0}".format(row), self.add_layout_pin(text="decode_{0}".format(output),
layer="m1", layer="m1",
offset=z_pin.ll(), offset=z_pin.ll(),
width=z_pin.width(), width=z_pin.width(),
height=z_pin.height()) height=z_pin.height())
def route_predecode_rails(self): def route_decoder_bus(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages.""" """
Creates vertical metal 2 bus to connect predecoder and decoder stages.
"""
# This is not needed for inputs <4 since they have no pre/decode stages. # This is not needed for inputs <4 since they have no pre/decode stages.
if (self.num_inputs >= 4): if (self.num_inputs >= 4):
input_offset = vector(0.5 * self.m2_width, 0) # 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_rails = self.create_vertical_pin_bus(layer="m2", self.predecode_bus = self.create_vertical_pin_bus(layer="m2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=input_offset, offset=vector(0, 0),
names=input_bus_names, names=input_bus_names,
length=self.height) length=self.height)
self.route_rails_to_predecodes() self.route_predecodes_to_bus()
self.route_rails_to_decoder() self.route_bus_to_decoder()
def route_rails_to_predecodes(self):
""" Iterates through all of the predecodes and connects to the rails including the offsets """
def route_predecodes_to_bus(self):
"""
Iterates through all of the predecodes
and connects to the rails including the offsets
"""
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre2x4): for pre_num in range(self.no_of_pre2x4):
for i in range(4): for i in range(4):
predecode_name = "predecode_{}".format(pre_num * 4 + i) predecode_name = "predecode_{}".format(pre_num * 4 + i)
out_name = "out_{}".format(i) out_name = "out_{}".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name) pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin) x_offset = self.pre2x4_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
# FIXME: convert to connect_bus # FIXME: convert to connect_bus
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
@ -449,52 +497,82 @@ class hierarchical_decoder(design.design):
predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4) predecode_name = "predecode_{}".format(pre_num * 8 + i + self.no_of_pre2x4 * 4)
out_name = "out_{}".format(i) out_name = "out_{}".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name) pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.route_predecode_rail_m3(predecode_name, pin) x_offset = self.pre3x8_inst[pre_num].rx() + self.m2_pitch
self.route_predecode_bus_inputs(predecode_name, pin, x_offset)
def route_rails_to_decoder(self): def route_bus_to_decoder(self):
""" Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups [ [0,1,2,3] ,[4,5,6,7],
[8,9,10,11,12,13,14,15] ] the first AND3 inputs are connected to
[0,4,8] and second AND3 is connected to [0,4,9] ........... and the
128th AND3 is connected to [3,7,15]
""" """
row_index = 0 Use the self.predec_groups to determine the connections to the decoder AND gates.
Inputs of AND2/AND3 gates come from different groups.
For example for these groups
[ [0,1,2,3] ,[4,5,6,7], [8,9,10,11,12,13,14,15] ]
the first AND3 inputs are connected to [0,4,8],
second AND3 is connected to [0,4,9],
...
and the 128th AND3 is connected to [3,7,15]
"""
output_index = 0
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 (row_index < self.rows): 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_rail(predecode_name, self.and_inst[row_index].get_pin("A")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) self.route_predecode_bus_outputs(predecode_name,
row_index = row_index + 1 self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
output_index = output_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
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 (row_index < self.rows): 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_rail(predecode_name, self.and_inst[row_index].get_pin("A")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("A"),
row_offset)
predecode_name = "predecode_{}".format(index_B) predecode_name = "predecode_{}".format(index_B)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("B")) self.route_predecode_bus_outputs(predecode_name,
self.and_inst[output_index].get_pin("B"),
row_offset + self.m3_pitch)
predecode_name = "predecode_{}".format(index_C) predecode_name = "predecode_{}".format(index_C)
self.route_predecode_rail(predecode_name, self.and_inst[row_index].get_pin("C")) self.route_predecode_bus_outputs(predecode_name,
row_index = row_index + 1 self.and_inst[output_index].get_pin("C"),
row_offset + 2 * self.m3_pitch)
output_index = output_index + 1
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.
"""
# The vias will be placed in the center and right of the cells, respectively. # The vias will be placed at the right of the cells.
xoffset = self.and_inst[0].rx() xoffset = max(x.rx() for x in self.and_inst)
for num in range(0, self.rows): for num in range(0, self.num_outputs):
# Only add the power pin for the 1st in each row
if num % self.decoders_per_row:
continue
for pin_name in ["vdd", "gnd"]: for pin_name in ["vdd", "gnd"]:
# 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",
[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)
@ -503,23 +581,42 @@ class hierarchical_decoder(design.design):
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_rail(self, rail_name, pin): def route_predecode_bus_outputs(self, rail_name, pin, y_offset):
""" Connect the routing rail to the given metal1 pin """ """
rail_pos = vector(self.predecode_rails[rail_name].x, pin.lc().y) Connect the routing rail to the given metal1 pin
self.add_path("m1", [rail_pos, pin.lc()]) using a routing track at the given y_offset
self.add_via_center(layers=self.m1_stack,
offset=rail_pos) """
pin_pos = pin.center()
# If we have a single decoder per row, we can route on M1
if self.decoders_per_row == 1:
rail_pos = vector(self.predecode_bus[rail_name].x, pin_pos.y)
self.add_path("m1", [rail_pos, pin_pos])
self.add_via_center(layers=self.m1_stack,
offset=rail_pos)
# If not, we must route over the decoder cells on M3
else:
rail_pos = vector(self.predecode_bus[rail_name].x, y_offset)
mid_pos = vector(pin_pos.x, rail_pos.y)
self.add_wire(self.m2_stack[::-1], [rail_pos, mid_pos, pin_pos])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos)
self.add_via_center(layers=self.m1_stack,
offset=pin_pos)
def route_predecode_rail_m3(self, rail_name, pin): def route_predecode_bus_inputs(self, rail_name, pin, x_offset):
""" Connect the routing rail to the given metal1 pin """ """
Connect the routing rail to the given metal1 pin using a jog
to the right of the cell at the given x_offset.
"""
# 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.
mid_point = vector(pin.cx(), pin.cy() + self.inv.height / 2) pin_pos = pin.center()
rail_pos = vector(self.predecode_rails[rail_name].x, mid_point.y) 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_wire(self.m1_stack, [pin_pos, mid_point1, mid_point2, rail_pos])
self.add_via_center(layers=self.m1_stack, self.add_via_center(layers=self.m1_stack,
offset=pin.center())
self.add_wire(("m3", "via2", "m2"), [rail_pos, mid_point, pin.uc()])
self.add_via_center(layers=self.m2_stack,
offset=rail_pos) offset=rail_pos)
def input_load(self): def input_load(self):

View File

@ -90,17 +90,14 @@ class port_address(design.design):
# The pre/post is to access the pin from "outside" the cell to avoid DRCs # The pre/post is to access the pin from "outside" the cell to avoid DRCs
decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc() decoder_out_pos = self.row_decoder_inst.get_pin("decode_{}".format(row)).rc()
driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc() driver_in_pos = self.wordline_driver_inst.get_pin("in_{}".format(row)).lc()
mid1 = decoder_out_pos.scale(0.5,1)+driver_in_pos.scale(0.5,0) mid1 = decoder_out_pos.scale(0.5, 1) + driver_in_pos.scale(0.5, 0)
mid2 = decoder_out_pos.scale(0.5,0)+driver_in_pos.scale(0.5,1) mid2 = decoder_out_pos.scale(0.5, 0) + driver_in_pos.scale(0.5, 1)
self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos]) self.add_path("m1", [decoder_out_pos, mid1, mid2, driver_in_pos])
def add_modules(self): def add_modules(self):
self.row_decoder = factory.create(module_type="decoder", self.row_decoder = factory.create(module_type="decoder",
rows=self.num_rows) num_outputs=self.num_rows)
self.add_mod(self.row_decoder) self.add_mod(self.row_decoder)
self.wordline_driver = factory.create(module_type="wordline_driver", self.wordline_driver = factory.create(module_type="wordline_driver",
@ -108,11 +105,10 @@ class port_address(design.design):
cols=self.num_cols) cols=self.num_cols)
self.add_mod(self.wordline_driver) self.add_mod(self.wordline_driver)
def create_row_decoder(self): def create_row_decoder(self):
""" Create the hierarchical row decoder """ """ Create the hierarchical row decoder """
self.row_decoder_inst = self.add_inst(name="row_decoder", self.row_decoder_inst = self.add_inst(name="row_decoder",
mod=self.row_decoder) mod=self.row_decoder)
temp = [] temp = []

View File

@ -126,14 +126,16 @@ class sense_amp_array(design.design):
for i in range(len(self.local_insts)): for i in range(len(self.local_insts)):
inst = self.local_insts[i] inst = self.local_insts[i]
gnd_pin = inst.get_pin("gnd")
self.add_power_pin(name="gnd", self.add_power_pin(name="gnd",
loc=inst.get_pin("gnd").center(), loc=gnd_pin.center(),
start_layer="m2", start_layer=gnd_pin.layer,
vertical=True) vertical=True)
vdd_pin = inst.get_pin("vdd")
self.add_power_pin(name="vdd", self.add_power_pin(name="vdd",
loc=inst.get_pin("vdd").center(), loc=vdd_pin.center(),
start_layer="m2", start_layer=vdd_pin.layer,
vertical=True) vertical=True)
bl_pin = inst.get_pin(inst.mod.get_bl_names()) bl_pin = inst.get_pin(inst.mod.get_bl_names())

View File

@ -166,7 +166,7 @@ class write_driver_array(design.design):
self.add_power_pin(name=n, self.add_power_pin(name=n,
loc=pin.center(), loc=pin.center(),
vertical=True, vertical=True,
start_layer="m2") 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):
inst = self.driver_insts[bit * self.write_size] inst = self.driver_insts[bit * self.write_size]

View File

@ -15,6 +15,7 @@ from globals import OPTS
from utils import round_to_grid from utils import round_to_grid
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
from errors import drc_error
class pinv(pgate.pgate): class pinv(pgate.pgate):
@ -105,11 +106,14 @@ class pinv(pgate.pgate):
# This is a poly-to-poly of a flipped cell # 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.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.poly_extend_active + self.poly_space) self.poly_extend_active + self.poly_space)
total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height > total_height, total_height = tx_height + min_channel + 2 * self.top_bottom_space
"Cell height {0} too small for simple min height {1}.".format(self.height, # debug.check(self.height > total_height,
total_height)) # "Cell height {0} too small for simple min height {1}.".format(self.height,
# total_height))
if total_height > self.height:
msg = "Cell height {0} too small for simple min height {1}.".format(self.height, total_height)
raise drc_error(msg)
# Determine the height left to the transistors to determine # Determine the height left to the transistors to determine
# the number of fingers # the number of fingers
@ -141,12 +145,16 @@ class pinv(pgate.pgate):
# with LVS property mismatch errors when fingers are not a grid # with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry. # length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults) self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width >= drc("minwidth_tx"), # debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS transistors to fit cell height.") # "Cannot finger NMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults) if self.nmos_width < drc("minwidth_tx"):
debug.check(self.pmos_width >= drc("minwidth_tx"), raise drc_error("Cannot finger NMOS transistors to fit cell height.")
"Cannot finger PMOS transistors to fit cell height.")
self.pmos_width = round_to_grid(self.pmos_width / self.tx_mults)
#debug.check(self.pmos_width >= drc("minwidth_tx"),
# "Cannot finger PMOS transistors to fit cell height.")
if self.pmos_width < drc("minwidth_tx"):
raise drc_error("Cannot finger NMOS transistors to fit cell height.")
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """

View File

@ -98,7 +98,7 @@ class pnand2(pgate.pgate):
# metal spacing to allow contacts on any layer # metal spacing to allow contacts on any layer
self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width, self.input_spacing = max(self.poly_space + contact.poly_contact.first_layer_width,
self.m1_space + contact.m1_via.first_layer_width, self.m1_space + contact.m1_via.first_layer_width,
self.m2_space + contact.m2_via.first_layer_width, self.m2_space + contact.m2_via.first_layer_width,
self.m3_space + contact.m2_via.second_layer_width) self.m3_space + contact.m2_via.second_layer_width)
@ -173,13 +173,15 @@ class pnand2(pgate.pgate):
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos_nd.active_height)) 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.pmos2_pos) self.add_nwell_contact(self.pmos,
self.add_pwell_contact(self.nmos_nd, self.nmos2_pos) self.pmos2_pos + vector(self.m1_pitch, 0))
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 """
@ -197,7 +199,7 @@ class pnand2(pgate.pgate):
self.nmos2_inst, self.nmos2_inst,
inputB_yoffset, inputB_yoffset,
"B", "B",
position="right") position="center")
# 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 = self.pmos2_inst.by() - self.poly_extend_active \ self.inputA_yoffset = self.pmos2_inst.by() - self.poly_extend_active \
@ -209,6 +211,7 @@ class pnand2(pgate.pgate):
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# 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.center()
@ -217,29 +220,46 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center() bottom_pin_offset = nmos_pin.center()
# Output pin # Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch, c_pin = self.get_pin("B")
out_offset = vector(c_pin.cx() + self.m1_pitch,
self.inputA_yoffset) self.inputA_yoffset)
# Midpoints of the L routes go horizontal first then vertical # This routes on M2
mid1_offset = vector(out_offset.x, top_pin_offset.y) # # Midpoints of the L routes go horizontal first then vertical
# mid1_offset = vector(out_offset.x, top_pin_offset.y)
# mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=pmos_pin.center())
# # Non-preferred active contacts
# self.add_via_center(layers=self.m1_stack,
# directions=("V", "H"),
# offset=nmos_pin.center())
# self.add_via_center(layers=self.m1_stack,
# offset=out_offset)
# # PMOS1 to mid-drain to NMOS2 drain
# self.add_path("m2",
# [top_pin_offset, mid1_offset, out_offset,
# mid2_offset, bottom_pin_offset])
# This routes on M1
# Midpoints of the L routes goes vertical first then horizontal
mid1_offset = vector(top_pin_offset.x, out_offset.y)
# Midpoints of the L routes goes horizontal first then vertical
mid2_offset = vector(out_offset.x, bottom_pin_offset.y) mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
# Non-preferred active contacts self.add_path("m1",
self.add_via_center(layers=self.m1_stack, [top_pin_offset, mid1_offset, out_offset])
directions=("V", "H"), # Route in two segments to have the width rule
offset=pmos_pin.center()) self.add_path("m1",
# Non-preferred active contacts [bottom_pin_offset, mid2_offset + vector(0.5 * self.m1_width, 0)],
self.add_via_center(layers=self.m1_stack, width=nmos_pin.height())
directions=("V", "H"), self.add_path("m1",
offset=nmos_pin.center()) [mid2_offset, out_offset])
self.add_via_center(layers=self.m1_stack,
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("m2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer="m1",

View File

@ -12,6 +12,7 @@ from tech import drc, parameter, spice
from vector import vector from vector import vector
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
from globals import OPTS
class pnand3(pgate.pgate): class pnand3(pgate.pgate):
@ -190,8 +191,10 @@ 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.pmos3_pos) self.add_nwell_contact(self.pmos,
self.add_pwell_contact(self.nmos_ns, self.nmos3_pos) self.pmos3_pos + vector(self.m1_pitch, 0))
self.add_pwell_contact(self.nmos_ns,
self.nmos3_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 """
@ -220,18 +223,22 @@ class pnand3(pgate.pgate):
self.nmos3_inst, self.nmos3_inst,
self.inputC_yoffset, self.inputC_yoffset,
"C", "C",
position="center") position="right")
# FIXME: constant hack # FIXME: constant hack
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch if OPTS.tech_name == "s8":
self.inputA_yoffset = self.inputB_yoffset + 1.15 * m1_pitch
else:
self.inputA_yoffset = self.inputB_yoffset + 1.12 * m1_pitch
self.route_input_gate(self.pmos1_inst, self.route_input_gate(self.pmos1_inst,
self.nmos1_inst, self.nmos1_inst,
self.inputA_yoffset, self.inputA_yoffset,
"A", "A",
position="center") position="left")
def route_output(self): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D") pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain # PMOS3 drain
@ -239,29 +246,56 @@ class pnand3(pgate.pgate):
# NMOS3 drain # NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # midpoint for routing
self.add_via_center(layers=self.m1_stack, mid_offset = vector(nmos3_pin.cx() + self.m1_pitch,
offset=pmos1_pin.center(), self.inputA_yoffset)
directions=("V", "V"))
self.add_via_center(layers=self.m1_stack, # Aligned with the well taps
offset=pmos3_pin.center(), out_offset = vector(self.nwell_contact.cx(),
directions=("V", "V")) self.inputA_yoffset)
self.add_via_center(layers=self.m1_stack,
offset=nmos3_pin.center(),
directions=("V", "V"))
# PMOS3 and NMOS3 are drain aligned # Go up to metal2 for ease on all output pins
self.add_path("m2", [pmos3_pin.center(), nmos3_pin.center()]) # self.add_via_center(layers=self.m1_stack,
# Route in the A input track (top track) # offset=pmos1_pin.center(),
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset) # directions=("V", "V"))
self.add_path("m2", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()]) # self.add_via_center(layers=self.m1_stack,
# offset=pmos3_pin.center(),
# directions=("V", "V"))
# self.add_via_center(layers=self.m1_stack,
# offset=nmos3_pin.center(),
# directions=("V", "V"))
# # Route in the A input track (top track)
# mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
# self.add_path("m1", [pmos1_pin.center(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_via_center(layers=self.m1_stack, # self.add_via_center(layers=self.m1_stack,
offset=mid_offset) # offset=mid_offset)
top_left_pin_offset = pmos1_pin.center()
top_right_pin_offset = pmos3_pin.center()
bottom_pin_offset = nmos3_pin.center()
# PMOS1 to output
self.add_path("m1", [top_left_pin_offset,
vector(top_left_pin_offset.x, out_offset.y),
out_offset])
# PMOS3 to output
self.add_path("m1", [top_right_pin_offset,
vector(top_right_pin_offset.x, mid_offset.y),
mid_offset])
# NMOS3 to output
mid2_offset = vector(mid_offset.x, bottom_pin_offset.y)
self.add_path("m1",
[bottom_pin_offset, mid2_offset],
width=nmos3_pin.height())
mid3_offset = vector(mid_offset.x, nmos3_pin.by())
self.add_path("m1", [mid3_offset, mid_offset])
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="m1", layer="m1",
offset=mid_offset, offset=out_offset,
width=contact.m1_via.first_layer_width, width=contact.m1_via.first_layer_width,
height=contact.m1_via.first_layer_height) height=contact.m1_via.first_layer_height)

View File

@ -11,6 +11,7 @@ from tech import layer, drc, spice
from vector import vector from vector import vector
from sram_factory import factory from sram_factory import factory
import contact import contact
import logical_effort
import os import os
from globals import OPTS from globals import OPTS
@ -106,20 +107,32 @@ class ptx(design.design):
# be decided in the layout later. # be decided in the layout later.
area_sd = 2.5 * self.poly_width * self.tx_width area_sd = 2.5 * self.poly_width * self.tx_width
perimeter_sd = 2 * self.poly_width + 2 * self.tx_width perimeter_sd = 2 * self.poly_width + 2 * self.tx_width
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type], if OPTS.tech_name == "s8":
self.mults, # s8 technology is in microns
self.tx_width, main_str = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type],
drc("minwidth_poly")) self.mults,
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd, self.tx_width,
area_sd) drc("minwidth_poly"))
# Perimeters are in microns
# Area is in u since it is microns square
area_str = "pd={0:.2f} ps={0:.2f} as={1:.2f}u ad={1:.2f}u".format(perimeter_sd,
area_sd)
else:
main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"))
area_str = "pd={0:.2f}u ps={0:.2f}u as={1:.2f}p ad={1:.2f}p".format(perimeter_sd,
area_sd)
self.spice_device = main_str + area_str self.spice_device = main_str + area_str
self.spice.append("\n* ptx " + self.spice_device) self.spice.append("\n* ptx " + self.spice_device)
# LVS lib is always in SI units
if os.path.exists(OPTS.openram_tech + "lvs_lib"): if os.path.exists(OPTS.openram_tech + "lvs_lib"):
self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2} l={3} ".format(spice[self.tx_type], self.lvs_device = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.mults, self.mults,
self.tx_width, self.tx_width,
drc("minwidth_poly")) drc("minwidth_poly"))
def setup_layout_constants(self): def setup_layout_constants(self):
@ -453,6 +466,26 @@ class ptx(design.design):
if self.connect_active: if self.connect_active:
self.connect_fingered_active(drain_positions, source_positions) self.connect_fingered_active(drain_positions, source_positions)
def get_stage_effort(self, cout):
"""Returns an object representing the parameters for delay in tau units."""
# FIXME: Using the same definition as the pinv.py.
parasitic_delay = 1
size = self.mults * self.tx_width / drc("minwidth_tx")
return logical_effort.logical_effort(self.name,
size,
self.input_load(),
cout,
parasitic_delay)
def input_load(self):
"""
Returns the relative gate cin of the tx
"""
# FIXME: this will be applied for the loads of the drain/source
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, contact=self.add_via_center(layers=self.active_stack,
@ -463,14 +496,25 @@ class ptx(design.design):
well_type=self.well_type) well_type=self.well_type)
if hasattr(self, "li_stack"): if hasattr(self, "li_stack"):
self.add_via_center(layers=self.li_stack, contact=self.add_via_center(layers=self.li_stack,
offset=pos) offset=pos,
directions=("V", "V"))
# contact_area = contact.mod.second_layer_width * contact.mod.second_layer_height
# min_area = drc("minarea_m1")
# width = contact.mod.second_layer_width
# if contact_area < min_area:
# height = min_area / width
# else:
# height = contact.mod.second_layer_height
width = contact.mod.second_layer_width
height = contact.mod.second_layer_height
self.add_layout_pin_rect_center(text=label, self.add_layout_pin_rect_center(text=label,
layer="m1", layer="m1",
offset=pos, offset=pos,
width=contact.mod.second_layer_width, width=width,
height=contact.mod.second_layer_height) height=height)
return(contact) return(contact)
def get_cin(self): def get_cin(self):

View File

@ -28,39 +28,39 @@ class hierarchical_decoder_test(openram_test):
factory.reset() factory.reset()
debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 16 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=16) a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 17 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=17) a = factory.create(module_type="hierarchical_decoder", num_outputs=17)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 23 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=23) a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 32 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=32) a = factory.create(module_type="hierarchical_decoder", num_outputs=32)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 65 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=65) a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 128 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=128) a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a) self.local_check(a)
factory.reset() factory.reset()
debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 341 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=341) a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)") debug.info(1, "Testing 512 row sample for hierarchical_decoder (multi-port case)")
a = factory.create(module_type="hierarchical_decoder", rows=512) a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -20,47 +20,38 @@ class hierarchical_decoder_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)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 4 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd1, rows=4)
# self.local_check(a)
# Doesn't require hierarchical decoder
# debug.info(1, "Testing 8 row sample for hierarchical_decoder")
# a = hierarchical_decoder.hierarchical_decoder(name="hd2", rows=8)
# self.local_check(a)
# check hierarchical decoder for single port # check hierarchical decoder for single port
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", rows=16) a = factory.create(module_type="hierarchical_decoder", num_outputs=16)
self.local_check(a) self.local_check(a)
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", rows=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") debug.info(1, "Testing 23 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=23) a = factory.create(module_type="hierarchical_decoder", num_outputs=23)
self.local_check(a) 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", rows=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") debug.info(1, "Testing 65 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=65) a = factory.create(module_type="hierarchical_decoder", num_outputs=65)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 128 row sample for hierarchical_decoder") debug.info(1, "Testing 128 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=128) a = factory.create(module_type="hierarchical_decoder", num_outputs=128)
self.local_check(a) self.local_check(a)
debug.info(1, "Testing 341 row sample for hierarchical_decoder") debug.info(1, "Testing 341 row sample for hierarchical_decoder")
a = factory.create(module_type="hierarchical_decoder", rows=341) a = factory.create(module_type="hierarchical_decoder", num_outputs=341)
self.local_check(a) self.local_check(a)
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", rows=512) a = factory.create(module_type="hierarchical_decoder", num_outputs=512)
self.local_check(a) self.local_check(a)
globals.end_openram() globals.end_openram()

View File

@ -51,7 +51,7 @@ class openram_back_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME) exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else: else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_back_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
@ -64,33 +64,32 @@ class openram_back_end_test(openram_test):
# assert an error until we actually check a resul # assert an error until we actually check a resul
for extension in ["gds", "v", "lef", "sp"]: for extension in ["gds", "v", "lef", "sp"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension) filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1,"Checking for file: " + filename) debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True) self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file # Make sure there is any .lib file
import glob import glob
files = glob.glob('{0}/*.lib'.format(out_path)) files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0) self.assertTrue(len(files)>0)
# Make sure there is any .html file # Make sure there is any .html file
if os.path.exists(out_path): if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path)) datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0) self.assertTrue(len(datasheets)>0)
# grep any errors from the output # grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r") output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read() output = output_log.read()
output_log.close() output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0) self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory # now clean up the directory
if OPTS.purge_temp: if OPTS.purge_temp:
if os.path.exists(out_path): if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False) self.assertEqual(os.path.exists(out_path), False)
globals.end_openram() globals.end_openram()

View File

@ -51,7 +51,7 @@ class openram_front_end_test(openram_test):
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage") debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")
exe_name = "{0}/openram.py ".format(OPENRAM_HOME) exe_name = "{0}/openram.py ".format(OPENRAM_HOME)
else: else:
exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME) exe_name = "coverage run -p {0}/openram.py ".format(OPENRAM_HOME)
config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME) config_name = "{0}/tests/configs/config_front_end.py".format(OPENRAM_HOME)
cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name, cmd = "{0} -n -o {1} -p {2} {3} {4} 2>&1 > {5}/output.log".format(exe_name,
out_file, out_file,
@ -64,33 +64,32 @@ class openram_front_end_test(openram_test):
# assert an error until we actually check a result # assert an error until we actually check a result
for extension in ["v", "lef", "sp", "gds"]: for extension in ["v", "lef", "sp", "gds"]:
filename = "{0}/{1}.{2}".format(out_path,out_file,extension) filename = "{0}/{1}.{2}".format(out_path, out_file, extension)
debug.info(1,"Checking for file: " + filename) debug.info(1, "Checking for file: " + filename)
self.assertEqual(os.path.exists(filename),True) self.assertEqual(os.path.exists(filename), True)
# Make sure there is any .lib file # Make sure there is any .lib file
import glob import glob
files = glob.glob('{0}/*.lib'.format(out_path)) files = glob.glob('{0}/*.lib'.format(out_path))
self.assertTrue(len(files)>0) self.assertTrue(len(files)>0)
# Make sure there is any .html file # Make sure there is any .html file
if os.path.exists(out_path): if os.path.exists(out_path):
datasheets = glob.glob('{0}/*html'.format(out_path)) datasheets = glob.glob('{0}/*html'.format(out_path))
self.assertTrue(len(datasheets)>0) self.assertTrue(len(datasheets)>0)
# grep any errors from the output # grep any errors from the output
output_log = open("{0}/output.log".format(out_path),"r") output_log = open("{0}/output.log".format(out_path), "r")
output = output_log.read() output = output_log.read()
output_log.close() output_log.close()
self.assertEqual(len(re.findall('ERROR',output)),0) self.assertEqual(len(re.findall('ERROR', output)), 0)
self.assertEqual(len(re.findall('WARNING',output)),0) self.assertEqual(len(re.findall('WARNING', output)), 0)
# now clean up the directory
# now clean up the directory
if OPTS.purge_temp: if OPTS.purge_temp:
if os.path.exists(out_path): if os.path.exists(out_path):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False) self.assertEqual(os.path.exists(out_path), False)
globals.end_openram() globals.end_openram()