OpenRAM v1.1.1

Fixed numerous P&R file format bugs.
Significant code cleanup and refactor.
Use new scn4m models.
This commit is contained in:
Matt Guthaus 2019-10-07 12:24:07 -07:00
commit c263cfaf8d
57 changed files with 2235 additions and 1583 deletions

View File

@ -246,4 +246,4 @@ If I forgot to add you, please let me know!
[FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf
[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LTE4ODMyM2I0Mzk2ZmFiMjgwYTYyMTQ4NTgwMmUwMDhiM2E1MDViNDRjYzU1NjJhZTQxNWZjMzE3M2FlODBmZjA
[Slack]: https://join.slack.com/t/openram/shared_invite/enQtNDgxMjc3NzU5NTI1LWZiYWMwNjNkZThmYTdkODc3NDE1NDhjNzUxNDhmMDQ4ZTM3NDgwNWFlNjM5NWFiZDkyMzBlNzc1NTg3ZjllNTY

View File

@ -7,39 +7,44 @@
#
import hierarchy_design
import debug
import utils
from tech import drc,layer
from tech import drc, layer
from vector import vector
class contact(hierarchy_design.hierarchy_design):
"""
Object for a contact shape with its conductor enclosures.
Creates a contact array minimum active or poly enclosure and metal1 enclosure.
This class has enclosure on two or four sides of the contact.
The direction specifies whether the first and second layer have asymmetric extension in the H or V direction.
Object for a contact shape with its conductor enclosures. Creates
a contact array minimum active or poly enclosure and metal1
enclosure. This class has enclosure on two or four sides of the
contact. The direction specifies whether the first and second
layer have asymmetric extension in the H or V direction.
The well/implant_type is an option to add a select/implant layer
enclosing the contact. This is necessary to import layouts into
Magic which requires the select to be in the same GDS hierarchy as
the contact.
The well/implant_type is an option to add a select/implant layer enclosing the contact. This is
necessary to import layouts into Magic which requires the select to be in the same GDS
hierarchy as the contact.
"""
def __init__(self, layer_stack, dimensions=(1,1), directions=("V","V"), implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since we can guarantee a unique name here
def __init__(self, layer_stack, dimensions=(1, 1), directions=("V", "V"),
implant_type=None, well_type=None, name=""):
# This will ignore the name parameter since
# we can guarantee a unique name here
hierarchy_design.hierarchy_design.__init__(self, name)
debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack))
self.add_comment("dimensions: {0}".format(dimensions))
if implant_type or well_type:
self.add_comment("implant type: {0}\nwell_type: {1}".format(implant_type,well_type))
self.add_comment("implant type: {}\n".format(implant_type))
self.add_comment("well_type: {}\n".format(well_type))
self.layer_stack = layer_stack
self.dimensions = dimensions
self.directions = directions
self.offset = vector(0,0)
self.offset = vector(0, 0)
self.implant_type = implant_type
self.well_type = well_type
self.well_type = well_type
# Module does not have pins, but has empty pin list.
self.pins = []
self.create_layout()
@ -59,7 +64,7 @@ class contact(hierarchy_design.hierarchy_design):
if self.implant_type and self.well_type:
self.create_implant_well_enclosures()
elif self.implant_type or self.well_type:
debug.error(-1,"Must define both implant and well type or none at all.")
debug.error(-1, "Must define both implant and well type or none at all.")
def setup_layers(self):
""" Locally assign the layer names. """
@ -67,10 +72,11 @@ class contact(hierarchy_design.hierarchy_design):
(first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer
self.via_layer_name = via_layer
# Some technologies have a separate active contact from the poly contact
# Some technologies have a separate active
# contact from the poly contact
# We will use contact for DRC, but active_contact for output
if first_layer=="active" or second_layer=="active":
self.via_layer_name_expanded = "active_"+via_layer
if first_layer == "active" or second_layer == "active":
self.via_layer_name_expanded = "active_" + via_layer
else:
self.via_layer_name_expanded = via_layer
self.second_layer_name = second_layer
@ -97,19 +103,19 @@ class contact(hierarchy_design.hierarchy_design):
second_layer_enclosure = drc("{0}_enclosure_{1}".format(self.second_layer_name, self.via_layer_name))
second_layer_extend = drc("{0}_extend_{1}".format(self.second_layer_name, self.via_layer_name))
# In some technologies, the minimum width may be larger than the overlap requirement around the via, so
# In some technologies, the minimum width may be larger
# than the overlap requirement around the via, so
# check this for each dimension.
if self.directions[0] == "V":
self.first_layer_horizontal_enclosure = max(first_layer_enclosure,
(first_layer_minwidth - self.contact_array_width)/2)
(first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(first_layer_extend,
(first_layer_minwidth - self.contact_array_height)/2)
(first_layer_minwidth - self.contact_array_height) / 2)
elif self.directions[0] == "H":
self.first_layer_horizontal_enclosure = max(first_layer_extend,
(first_layer_minwidth - self.contact_array_width)/2)
(first_layer_minwidth - self.contact_array_width) / 2)
self.first_layer_vertical_enclosure = max(first_layer_enclosure,
(first_layer_minwidth - self.contact_array_height)/2)
(first_layer_minwidth - self.contact_array_height) / 2)
else:
debug.error("Invalid first layer direction.", -1)
@ -117,23 +123,23 @@ class contact(hierarchy_design.hierarchy_design):
# check this for each dimension.
if self.directions[1] == "V":
self.second_layer_horizontal_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width)/2)
(second_layer_minwidth - self.contact_array_width) / 2)
self.second_layer_vertical_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height)/2)
(second_layer_minwidth - self.contact_array_height) / 2)
elif self.directions[1] == "H":
self.second_layer_horizontal_enclosure = max(second_layer_extend,
(second_layer_minwidth - self.contact_array_height)/2)
(second_layer_minwidth - self.contact_array_height) / 2)
self.second_layer_vertical_enclosure = max(second_layer_enclosure,
(second_layer_minwidth - self.contact_array_width)/2)
(second_layer_minwidth - self.contact_array_width) / 2)
else:
debug.error("Invalid second layer direction.", -1)
def create_contact_array(self):
""" Create the contact array at the origin"""
# offset for the via array
self.via_layer_position =vector(max(self.first_layer_horizontal_enclosure,self.second_layer_horizontal_enclosure),
max(self.first_layer_vertical_enclosure,self.second_layer_vertical_enclosure))
self.via_layer_position = vector(
max(self.first_layer_horizontal_enclosure, self.second_layer_horizontal_enclosure),
max(self.first_layer_vertical_enclosure, self.second_layer_vertical_enclosure))
for i in range(self.dimensions[1]):
offset = self.via_layer_position + vector(0, self.contact_pitch * i)
@ -142,15 +148,16 @@ class contact(hierarchy_design.hierarchy_design):
offset=offset,
width=self.contact_width,
height=self.contact_width)
offset = offset + vector(self.contact_pitch,0)
offset = offset + vector(self.contact_pitch, 0)
def create_first_layer_enclosure(self):
# this is if the first and second layers are different
self.first_layer_position = vector(max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure,0),
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0))
self.first_layer_position = vector(
max(self.second_layer_horizontal_enclosure - self.first_layer_horizontal_enclosure, 0),
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure, 0))
self.first_layer_width = self.contact_array_width + 2*self.first_layer_horizontal_enclosure
self.first_layer_height = self.contact_array_height + 2*self.first_layer_vertical_enclosure
self.first_layer_width = self.contact_array_width + 2 * self.first_layer_horizontal_enclosure
self.first_layer_height = self.contact_array_height + 2 * self.first_layer_vertical_enclosure
self.add_rect(layer=self.first_layer_name,
offset=self.first_layer_position,
width=self.first_layer_width,
@ -158,27 +165,28 @@ class contact(hierarchy_design.hierarchy_design):
def create_second_layer_enclosure(self):
# this is if the first and second layers are different
self.second_layer_position = vector(max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure,0),
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0))
self.second_layer_position = vector(
max(self.first_layer_horizontal_enclosure - self.second_layer_horizontal_enclosure, 0),
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure, 0))
self.second_layer_width = self.contact_array_width + 2*self.second_layer_horizontal_enclosure
self.second_layer_height = self.contact_array_height + 2*self.second_layer_vertical_enclosure
self.second_layer_width = self.contact_array_width + 2 * self.second_layer_horizontal_enclosure
self.second_layer_height = self.contact_array_height + 2 * self.second_layer_vertical_enclosure
self.add_rect(layer=self.second_layer_name,
offset=self.second_layer_position,
width=self.second_layer_width,
height=self.second_layer_height)
def create_implant_well_enclosures(self):
implant_position = self.first_layer_position - [drc("implant_enclosure_active")]*2
implant_width = self.first_layer_width + 2*drc("implant_enclosure_active")
implant_height = self.first_layer_height + 2*drc("implant_enclosure_active")
implant_position = self.first_layer_position - [drc("implant_enclosure_active")] * 2
implant_width = self.first_layer_width + 2 * drc("implant_enclosure_active")
implant_height = self.first_layer_height + 2 * drc("implant_enclosure_active")
self.add_rect(layer="{}implant".format(self.implant_type),
offset=implant_position,
width=implant_width,
height=implant_height)
well_position = self.first_layer_position - [drc("well_enclosure_active")]*2
well_width = self.first_layer_width + 2*drc("well_enclosure_active")
well_height = self.first_layer_height + 2*drc("well_enclosure_active")
well_position = self.first_layer_position - [drc("well_enclosure_active")] * 2
well_width = self.first_layer_width + 2 * drc("well_enclosure_active")
well_height = self.first_layer_height + 2 * drc("well_enclosure_active")
self.add_rect(layer="{}well".format(self.well_type),
offset=well_position,
width=well_width,
@ -188,16 +196,30 @@ class contact(hierarchy_design.hierarchy_design):
""" Get total power of a module """
return self.return_power()
from sram_factory import factory
# This is not instantiated and used for calculations only.
# These are static 1x1 contacts to reuse in all the design modules.
well = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V"))
active = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V"))
poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"), directions=("V","H"))
m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"), directions=("H","V"))
m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"), directions=("V","H"))
well = factory.create(module_type="contact",
layer_stack=("active", "contact", "metal1"),
directions=("H", "V"))
active = factory.create(module_type="contact",
layer_stack=("active", "contact", "metal1"),
directions=("H", "V"))
poly = factory.create(module_type="contact",
layer_stack=("poly", "contact", "metal1"),
directions=("V", "H"))
m1m2 = factory.create(module_type="contact",
layer_stack=("metal1", "via1", "metal2"),
directions=("H", "V"))
m2m3 = factory.create(module_type="contact",
layer_stack=("metal2", "via2", "metal3"),
directions=("V", "H"))
if "metal4" in layer.keys():
m3m4 = factory.create(module_type="contact", layer_stack=("metal3", "via3", "metal4"), directions=("H","V"))
m3m4 = factory.create(module_type="contact",
layer_stack=("metal3", "via3", "metal4"),
directions=("H", "V"))
else:
m3m4 = None

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
class delay_data():
"""
This is the delay class to represent the delay information
@ -20,13 +21,13 @@ class delay_data():
def __str__(self):
""" override print function output """
return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+""
return "Delta Data: Delay {} Slew {}".format(self.delay, self.slew)
def __add__(self, other):
"""
Override - function (left), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
assert isinstance(other, delay_data)
return delay_data(other.delay + self.delay,
other.slew)
@ -34,7 +35,7 @@ class delay_data():
"""
Override - function (right), for delay_data: a+b != b+a
"""
assert isinstance(other,delay_data)
assert isinstance(other, delay_data)
return delay_data(other.delay + self.delay,
self.slew)

View File

@ -7,12 +7,9 @@
#
from hierarchy_design import hierarchy_design
import contact
import globals
import verify
import debug
import os
from globals import OPTS
class design(hierarchy_design):
"""
This is the same as the hierarchy_design class except it contains
@ -21,29 +18,29 @@ class design(hierarchy_design):
"""
def __init__(self, name):
hierarchy_design.__init__(self,name)
hierarchy_design.__init__(self, name)
self.setup_drc_constants()
self.setup_multiport_constants()
from tech import layer
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space)
self.m1_pitch = max(contact.m1m2.width, contact.m1m2.height) + max(self.m1_space, self.m2_space)
self.m2_pitch = max(contact.m2m3.width, contact.m2m3.height) + max(self.m2_space, self.m3_space)
if "metal4" in layer:
self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space)
self.m3_pitch = max(contact.m3m4.width, contact.m3m4.height) + max(self.m3_space, self.m4_space)
else:
self.m3_pitch = self.m2_pitch
def setup_drc_constants(self):
""" These are some DRC constants used in many places in the compiler."""
from tech import drc,layer
from tech import drc, layer
self.well_width = drc("minwidth_well")
self.poly_width = drc("minwidth_poly")
self.poly_space = drc("poly_to_poly")
self.poly_space = drc("poly_to_poly")
self.m1_width = drc("minwidth_metal1")
self.m1_space = drc("metal1_to_metal1")
self.m2_width = drc("minwidth_metal2")
self.m2_space = drc("metal2_to_metal2")
self.m2_space = drc("metal2_to_metal2")
self.m3_width = drc("minwidth_metal3")
self.m3_space = drc("metal3_to_metal3")
if "metal4" in layer:
@ -93,12 +90,12 @@ class design(hierarchy_design):
port_number += 1
for port in range(OPTS.num_w_ports):
self.write_ports.append(port_number)
self.writeonly_ports.append(port_number)
self.writeonly_ports.append(port_number)
port_number += 1
for port in range(OPTS.num_r_ports):
self.read_ports.append(port_number)
self.readonly_ports.append(port_number)
port_number += 1
port_number += 1
def analytical_power(self, corner, load):
""" Get total power of a module """

View File

@ -6,7 +6,7 @@
# All rights reserved.
#
"""
This provides a set of useful generic types for the gdsMill interface.
This provides a set of useful generic types for the gdsMill interface.
"""
import debug
from vector import vector
@ -15,6 +15,7 @@ import math
from globals import OPTS
from utils import round_to_grid
class geometry:
"""
A specific path, shape, or text geometry. Base class for shared
@ -27,11 +28,11 @@ class geometry:
def __str__(self):
""" override print function output """
debug.error("__str__ must be overridden by all geometry types.",1)
debug.error("__str__ must be overridden by all geometry types.", 1)
def __repr__(self):
""" override print function output """
debug.error("__repr__ must be overridden by all geometry types.",1)
debug.error("__repr__ must be overridden by all geometry types.", 1)
# def translate_coords(self, coords, mirr, angle, xyShift):
# """Calculate coordinates after flip, rotate, and shift"""
@ -46,50 +47,52 @@ class geometry:
"""Calculate coordinates after flip, rotate, and shift"""
coordinate = []
for item in coords:
x = item[0]*math.cos(angle) - item[1]*mirr*math.sin(angle) + offset[0]
y = item[0]*math.sin(angle) + item[1]*mirr*math.cos(angle) + offset[1]
x = item[0] * math.cos(angle) - item[1] * mirr * math.sin(angle) + offset[0]
y = item[0] * math.sin(angle) + item[1] * mirr * math.cos(angle) + offset[1]
coordinate += [[x, y]]
return coordinate
def normalize(self):
""" Re-find the LL and UR points after a transform """
(first,second)=self.boundary
ll = vector(min(first[0],second[0]),min(first[1],second[1])).snap_to_grid()
ur = vector(max(first[0],second[0]),max(first[1],second[1])).snap_to_grid()
self.boundary=[ll,ur]
(first, second) = self.boundary
ll = vector(min(first[0], second[0]),
min(first[1], second[1])).snap_to_grid()
ur = vector(max(first[0], second[0]),
max(first[1], second[1])).snap_to_grid()
self.boundary = [ll, ur]
def update_boundary(self):
""" Update the boundary with a new placement. """
self.compute_boundary(self.offset,self.mirror,self.rotate)
self.compute_boundary(self.offset, self.mirror, self.rotate)
def compute_boundary(self,offset=vector(0,0),mirror="",rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location.
def compute_boundary(self, offset=vector(0, 0), mirror="", rotate=0):
""" Transform with offset, mirror and rotation to get the absolute pin location.
We must then re-find the ll and ur. The master is the cell instance. """
if OPTS.netlist_only:
return
(ll,ur) = [vector(0,0),vector(self.width,self.height)]
(ll, ur) = [vector(0, 0), vector(self.width, self.height)]
if mirror=="MX":
ll=ll.scale(1,-1)
ur=ur.scale(1,-1)
elif mirror=="MY":
ll=ll.scale(-1,1)
ur=ur.scale(-1,1)
elif mirror=="XY":
ll=ll.scale(-1,-1)
ur=ur.scale(-1,-1)
if mirror == "MX":
ll = ll.scale(1, -1)
ur = ur.scale(1, -1)
elif mirror == "MY":
ll = ll.scale(-1, 1)
ur = ur.scale(-1, 1)
elif mirror == "XY":
ll = ll.scale(-1, -1)
ur = ur.scale(-1, -1)
if rotate==90:
ll=ll.rotate_scale(-1,1)
ur=ur.rotate_scale(-1,1)
elif rotate==180:
ll=ll.scale(-1,-1)
ur=ur.scale(-1,-1)
elif rotate==270:
ll=ll.rotate_scale(1,-1)
ur=ur.rotate_scale(1,-1)
if rotate == 90:
ll = ll.rotate_scale(-1, 1)
ur = ur.rotate_scale(-1, 1)
elif rotate == 180:
ll = ll.scale(-1, -1)
ur = ur.scale(-1, -1)
elif rotate == 270:
ll = ll.rotate_scale(1, -1)
ur = ur.rotate_scale(1, -1)
self.boundary=[offset+ll,offset+ur]
self.boundary = [offset + ll, offset + ur]
self.normalize()
def ll(self):
@ -108,7 +111,6 @@ class geometry:
""" Return the upper left corner """
return vector(self.boundary[0].x, self.boundary[1].y)
def uy(self):
""" Return the upper edge """
return self.boundary[1].y
@ -127,11 +129,11 @@ class geometry:
def cx(self):
""" Return the center x """
return 0.5*(self.boundary[0].x + self.boundary[1].x)
return 0.5 * (self.boundary[0].x + self.boundary[1].x)
def cy(self):
""" Return the center y """
return 0.5*(self.boundary[0].y + self.boundary[1].y)
return 0.5 * (self.boundary[0].y + self.boundary[1].y)
class instance(geometry):
@ -139,10 +141,11 @@ class instance(geometry):
An instance of an instance/module with a specified location and
rotation
"""
def __init__(self, name, mod, offset=[0,0], mirror="R0", rotate=0):
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
"""Initializes an instance to represent a module"""
geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
debug.check(mirror not in ["R90", "R180", "R270"],
"Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
@ -154,13 +157,13 @@ class instance(geometry):
self.width = 0
self.height = 0
else:
if mirror in ["R90","R270"] or rotate in [90,270]:
if mirror in ["R90", "R270"] or rotate in [90, 270]:
self.width = round_to_grid(mod.height)
self.height = round_to_grid(mod.width)
else:
self.width = round_to_grid(mod.width)
self.height = round_to_grid(mod.height)
self.compute_boundary(offset,mirror,rotate)
self.compute_boundary(offset, mirror, rotate)
debug.info(4, "creating instance: " + self.name)
@ -169,18 +172,18 @@ class instance(geometry):
Apply the transform of the instance placement to give absolute blockages."""
angle = math.radians(float(self.rotate))
mirr = 1
if self.mirror=="R90":
if self.mirror == "R90":
angle += math.radians(90.0)
elif self.mirror=="R180":
elif self.mirror == "R180":
angle += math.radians(180.0)
elif self.mirror=="R270":
elif self.mirror == "R270":
angle += math.radians(270.0)
elif self.mirror=="MX":
elif self.mirror == "MX":
mirr = -1
elif self.mirror=="MY":
elif self.mirror == "MY":
mirr = -1
angle += math.radians(180.0)
elif self.mirror=="XY":
elif self.mirror == "XY":
mirr = 1
angle += math.radians(180.0)
@ -226,7 +229,7 @@ class instance(geometry):
this instance location. Index will return one of several pins."""
import copy
if index==-1:
if index == -1:
pin = copy.deepcopy(self.mod.get_pin(name))
pin.transform(self.offset,self.mirror,self.rotate)
return pin
@ -339,6 +342,7 @@ class label(geometry):
""" override print function output """
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
class rectangle(geometry):
"""Represents a rectangular shape"""
@ -351,22 +355,23 @@ class rectangle(geometry):
self.size = vector(width, height).snap_to_grid()
self.width = round_to_grid(self.size.x)
self.height = round_to_grid(self.size.y)
self.compute_boundary(offset,"",0)
self.compute_boundary(offset, "", 0)
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def get_blockages(self, layer):
""" Returns a list of one rectangle if it is on this layer"""
if self.layerNumber == layer:
return [[self.offset, vector(self.offset.x+self.width,self.offset.y+self.height)]]
return [[self.offset,
vector(self.offset.x + self.width,
self.offset.y + self.height)]]
else:
return []
def gds_write_file(self, new_layout):
"""Writes the rectangular shape to GDS"""
debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):"
debug.info(4, "writing rectangle (" + str(self.layerNumber) + "):"
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
new_layout.addBox(layerNumber=self.layerNumber,
purposeNumber=0,

View File

@ -122,6 +122,9 @@ class verilog:
if self.write_size:
self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port))
self.vf.write(" addr{0}_reg = addr{0};\n".format(port))
if port in self.read_ports:
self.add_write_read_checks(port)
if port in self.write_ports:
self.vf.write(" din{0}_reg = din{0};\n".format(port))
if port in self.read_ports:
@ -211,4 +214,29 @@ class verilog:
self.vf.write(" if (!csb{0}_reg)\n".format(port))
self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port))
self.vf.write(" end\n")
def add_address_check(self, wport, rport):
""" Output a warning if the two addresses match """
# If the rport is actually reading... and addresses match.
if rport in self.readwrite_ports:
rport_control = "!csb{0} && web{0}".format(rport)
else:
rport_control = "!csb{0}".format(rport)
if wport in self.readwrite_ports:
wport_control = "!csb{0} && !web{0}".format(wport)
else:
wport_control = "!csb{0}".format(wport)
self.vf.write(" if ({1} && {3} && (addr{0} == addr{2}))\n".format(wport,wport_control,rport,rport_control))
self.vf.write(" $display($time,\" WARNING: Writing and reading addr{0}=%b and addr{1}=%b simultaneously!\",addr{0},addr{1});\n".format(wport,rport))
def add_write_read_checks(self, rport):
"""
Add a warning if we read from an address that we are currently writing.
Can be fixed if we appropriately size the write drivers to do this .
"""
for wport in self.write_ports:
if wport == rport:
continue
else:
self.add_address_check(wport,rport)

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
import logical_effort
from tech import GDS, layer
import bitcell_base
class bitcell(design.design):
class bitcell(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
@ -21,13 +21,15 @@ class bitcell(design.design):
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
storage_nets = ['Q', 'Qbar']
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"])
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "cell_6t")
bitcell_base.bitcell_base.__init__(self, "cell_6t")
debug.info(2, "Create bitcell")
self.width = bitcell.width
@ -36,15 +38,9 @@ class bitcell(design.design):
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
return logical_effort.logical_effort('bitline', size, cin, load, parasitic_delay, False)
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl"]
row_pins = ["wl"]
return row_pins
def get_all_bitline_names(self):
@ -64,43 +60,22 @@ class bitcell(design.design):
def get_bl_name(self, port=0):
"""Get bl name"""
debug.check(port==0,"One port for bitcell only.")
debug.check(port == 0, "One port for bitcell only.")
return "bl"
def get_br_name(self, port=0):
"""Get bl name"""
debug.check(port==0,"One port for bitcell only.")
return "br"
debug.check(port == 0, "One port for bitcell only.")
return "br"
def get_wl_name(self, port=0):
"""Get wl name"""
debug.check(port==0,"One port for bitcell only.")
return "wl"
debug.check(port == 0, "One port for bitcell only.")
return "wl"
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
from tech import GDS, layer, parameter, drc
import logical_effort
import bitcell_base
class bitcell_1rw_1r(design.design):
class bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
@ -20,14 +21,17 @@ class bitcell_1rw_1r(design.design):
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width,height) = utils.get_libcell_size("cell_1rw_1r", GDS["unit"], layer["boundary"])
(width, height) = utils.get_libcell_size("cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "cell_1rw_1r")
bitcell_base.bitcell_base.__init__(self, "cell_1rw_1r")
debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width
@ -36,15 +40,11 @@ class bitcell_1rw_1r(design.design):
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
"bl1_{0}".format(col),
@ -57,7 +57,7 @@ class bitcell_1rw_1r(design.design):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
row_pins = ["wl0", "wl1"]
return row_pins
def get_all_bitline_names(self):
@ -97,52 +97,27 @@ class bitcell_1rw_1r(design.design):
def get_bl_name(self, port=0):
"""Get bl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "bl{}".format(port)
def get_br_name(self, port=0):
"""Get bl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "br{}".format(port)
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
# Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self)
graph.add_edge(pin_dict["wl0"], pin_dict["br0"], self)
# Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
import logical_effort
from tech import GDS, layer
import bitcell_base
class bitcell_1w_1r(design.design):
class bitcell_1w_1r(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
@ -20,14 +20,17 @@ class bitcell_1w_1r(design.design):
"""
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"])
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
storage_nets = ['Q', 'Q_bar']
(width, height) = utils.get_libcell_size("cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "cell_1w_1r")
bitcell_base.bitcell_base.__init__(self, "cell_1w_1r")
debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width
@ -36,15 +39,11 @@ class bitcell_1w_1r(design.design):
self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets)
def get_stage_effort(self, load):
parasitic_delay = 1
size = 0.5 #This accounts for bitline being drained thought the access TX and internal node
cin = 3 #Assumes always a minimum sizes inverter. Could be specified in the tech.py file.
read_port_load = 0.5 #min size NMOS gate load
return logical_effort.logical_effort('bitline', size, cin, load+read_port_load, parasitic_delay, False)
def get_bitcell_pins(self, col, row):
""" Creates a list of connections in the bitcell, indexed by column and row, for instance use in bitcell_array """
"""
Creates a list of connections in the bitcell,
indexed by column and row, for instance use in bitcell_array
"""
bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col),
"bl1_{0}".format(col),
@ -57,7 +56,7 @@ class bitcell_1w_1r(design.design):
def get_all_wl_names(self):
""" Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"]
row_pins = ["wl0", "wl1"]
return row_pins
def get_all_bitline_names(self):
@ -105,40 +104,15 @@ class bitcell_1w_1r(design.design):
def get_wl_name(self, port=0):
"""Get wl name by port"""
debug.check(port<2,"Two ports for bitcell_1rw_1r only.")
debug.check(port < 2, "Two ports for bitcell_1rw_1r only.")
return "wl{}".format(port)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
"""Return the relative capacitance of the access transistor gates"""
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def get_storage_net_names(self):
"""Returns names of storage nodes in bitcell in [non-inverting, inverting] format."""
#Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
debug.info(1,"Storage nodes={} not found in spice file.".format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports.
pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
# Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
# Port 1 is a write port, so its timing is not considered here.
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self)
graph.add_edge(pin_dict["wl1"], pin_dict["br1"], self)
# Port 1 is a write port, so its timing is not considered here.

View File

@ -0,0 +1,87 @@
# See LICENSE for licensing information.
#
# Copyright (c) 2016-2019 Regents of the University of California and The Board
# of Regents for the Oklahoma Agricultural and Mechanical College
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
import design
import logical_effort
from tech import parameter, drc
class bitcell_base(design.design):
"""
Base bitcell parameters to be over-riden.
"""
def __init__(self, name):
design.design.__init__(self, name)
def get_stage_effort(self, load):
parasitic_delay = 1
# This accounts for bitline being drained
# thought the access TX and internal node
size = 0.5
# Assumes always a minimum sizes inverter.
# Could be specified in the tech.py file.
cin = 3
# min size NMOS gate load
read_port_load = 0.5
return logical_effort.logical_effort('bitline',
size,
cin,
load + read_port_load,
parasitic_delay,
False)
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
# FIXME
dynamic = 0
total_power = self.return_power(dynamic, leakage)
return total_power
def input_load(self):
""" Return the relative capacitance of the access transistor gates """
# FIXME: This applies to bitline capacitances as well.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
# This is a handmade cell so the value must be entered
# in the tech.py file or estimated.
# Calculated in the tech file by summing the widths of all
# the related gates and dividing by the minimum width.
# FIXME: sizing is not accurate with the handmade cell.
# Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"] / drc["minwidth_tx"]
return 2 * access_tx_cin
def get_storage_net_names(self):
"""
Returns names of storage nodes in bitcell in
[non-inverting, inverting] format.
"""
# Checks that they do exist
if self.nets_match:
return self.storage_nets
else:
fmt_str = "Storage nodes={} not found in spice file."
debug.info(1, fmt_str.format(self.storage_nets))
return None
def build_graph(self, graph, inst_name, port_nets):
"""
By default, bitcells won't be part of the graph.
"""
return

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,parameter,drc
import logical_effort
from tech import GDS, layer
import bitcell_base
class dummy_bitcell(design.design):
class dummy_bitcell(bitcell_base.bitcell_base):
"""
A single bit cell (6T, 8T, etc.) This module implements the
single memory cell used in the design. It is a hand-made cell, so
@ -20,29 +20,18 @@ class dummy_bitcell(design.design):
"""
pin_names = ["bl", "br", "wl", "vdd", "gnd"]
(width,height) = utils.get_libcell_size("dummy_cell_6t", GDS["unit"], layer["boundary"])
(width, height) = utils.get_libcell_size("dummy_cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "dummy_cell_6t")
bitcell_base.bitcell_base.__init__(self, "dummy_cell_6t")
debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width
self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map
def analytical_power(self, corner, load):
"""Bitcell power in nW. Only characterizes leakage."""
from tech import spice
leakage = spice["bitcell_leakage"]
dynamic = 0 #temporary
total_power = self.return_power(dynamic, leakage)
return total_power
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin

View File

@ -5,12 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
from tech import GDS, layer
import bitcell_base
class dummy_bitcell_1rw_1r(design.design):
class dummy_bitcell_1rw_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -18,13 +19,18 @@ class dummy_bitcell_1rw_1r(design.design):
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"])
type_list = ["OUTPUT", "OUTPUT", "OUTPUT", "OUTPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1rw_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1rw_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "dummy_cell_1rw_1r")
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width
@ -32,14 +38,3 @@ class dummy_bitcell_1rw_1r(design.design):
self.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

View File

@ -5,12 +5,13 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import design
import debug
import utils
from tech import GDS,layer,drc,parameter
from tech import GDS, layer
import bitcell_base
class dummy_bitcell_1w_1r(design.design):
class dummy_bitcell_1w_1r(bitcell_base.bitcell_base):
"""
A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It
@ -18,13 +19,18 @@ class dummy_bitcell_1w_1r(design.design):
the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"])
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
"INPUT", "INPUT", "POWER", "GROUND"]
(width, height) = utils.get_libcell_size("dummy_cell_1w_1r",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names,
"dummy_cell_1w_1r",
GDS["unit"])
def __init__(self, name=""):
# Ignore the name argument
design.design.__init__(self, "dummy_cell_1w_1r")
# Ignore the name argument
bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width
@ -32,14 +38,4 @@ class dummy_bitcell_1w_1r(design.design):
self.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list)
def get_wl_cin(self):
"""Return the relative capacitance of the access transistor gates"""
#This is a handmade cell so the value must be entered in the tech.py file or estimated.
#Calculated in the tech file by summing the widths of all the related gates and dividing by the minimum width.
#FIXME: sizing is not accurate with the handmade cell. Change once cell widths are fixed.
access_tx_cin = parameter["6T_access_size"]/drc["minwidth_tx"]
return 2*access_tx_cin
def build_graph(self, graph, inst_name, port_nets):
"""Dummy bitcells are cannot form a path and be part of the timing graph"""
return

File diff suppressed because it is too large Load Diff

View File

@ -1180,44 +1180,57 @@ class delay(simulation):
wmask_zeroes = "0"*self.num_wmasks
if self.t_current == 0:
self.add_noop_all_ports("Idle cycle (no positive clock edge)",
inverse_address, data_zeros,wmask_zeroes)
self.add_noop_all_ports("Idle cycle (no positive clock edge)")
self.add_write("W data 1 address {}".format(inverse_address),
inverse_address,data_ones,wmask_ones,write_port)
inverse_address,
data_ones,
wmask_ones,
write_port)
self.add_write("W data 0 address {} to write value".format(self.probe_address),
self.probe_address,data_zeros,wmask_ones,write_port)
self.probe_address,
data_zeros,
wmask_ones,
write_port)
self.measure_cycles[write_port][sram_op.WRITE_ZERO] = len(self.cycle_times)-1
# This also ensures we will have a H->L transition on the next read
self.add_read("R data 1 address {} to set dout caps".format(inverse_address),
inverse_address,data_zeros,wmask_ones,read_port)
inverse_address,
read_port)
self.add_read("R data 0 address {} to check W0 worked".format(self.probe_address),
self.probe_address,data_zeros,wmask_ones,read_port)
self.probe_address,
read_port)
self.measure_cycles[read_port][sram_op.READ_ZERO] = len(self.cycle_times)-1
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)",
inverse_address,data_zeros,wmask_zeroes)
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)")
self.add_write("W data 1 address {} to write value".format(self.probe_address),
self.probe_address,data_ones,wmask_ones,write_port)
self.probe_address,
data_ones,
wmask_ones,
write_port)
self.measure_cycles[write_port][sram_op.WRITE_ONE] = len(self.cycle_times)-1
self.add_write("W data 0 address {} to clear din caps".format(inverse_address),
inverse_address,data_zeros,wmask_ones,write_port)
inverse_address,
data_zeros,
wmask_ones,
write_port)
# This also ensures we will have a L->H transition on the next read
self.add_read("R data 0 address {} to clear dout caps".format(inverse_address),
inverse_address,data_zeros,wmask_ones,read_port)
inverse_address,
read_port)
self.add_read("R data 1 address {} to check W1 worked".format(self.probe_address),
self.probe_address,data_zeros,wmask_ones,read_port)
self.probe_address,
read_port)
self.measure_cycles[read_port][sram_op.READ_ONE] = len(self.cycle_times)-1
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))",
self.probe_address,data_zeros,wmask_zeroes)
self.add_noop_all_ports("Idle cycle (if read takes >1 cycle))")
def get_available_port(self,get_read_port):

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
import sys,re,shutil
import copy
import collections
from design import design
import debug
@ -49,28 +50,19 @@ class functional(simulation):
self.create_graph()
self.set_internal_spice_names()
self.initialize_wmask()
# Number of checks can be changed
self.num_cycles = 15
# This is to have ordered keys for random selection
self.stored_words = collections.OrderedDict()
self.write_check = []
self.read_check = []
self.read_results = []
def initialize_wmask(self):
self.wmask = ""
if self.write_size:
# initialize all wmask bits to 1
for bit in range(self.num_wmasks):
self.wmask += "1"
def run(self, feasible_period=None):
if feasible_period: #period defaults to tech.py feasible period otherwise.
self.period = feasible_period
# Generate a random sequence of reads and writes
self.write_random_memory_sequence()
self.create_random_memory_sequence()
# Run SPICE simulation
self.write_functional_stimulus()
@ -83,8 +75,26 @@ class functional(simulation):
# Check read values with written values. If the values do not match, return an error.
return self.check_stim_results()
def write_random_memory_sequence(self):
def check_lengths(self):
""" Do a bunch of assertions. """
for port in self.all_ports:
checks = []
if port in self.read_ports:
checks.append((self.addr_value[port],"addr"))
if port in self.write_ports:
checks.append((self.data_value[port],"data"))
checks.append((self.wmask_value[port],"wmask"))
for (val, name) in checks:
debug.check(len(self.cycle_times)==len(val),
"Port {2} lengths don't match. {0} clock values, {1} {3} values".format(len(self.cycle_times),
len(val),
port,
name))
def create_random_memory_sequence(self):
if self.write_size:
rw_ops = ["noop", "write", "partial_write", "read"]
w_ops = ["noop", "write", "partial_write"]
@ -92,35 +102,45 @@ class functional(simulation):
rw_ops = ["noop", "write", "read"]
w_ops = ["noop", "write"]
r_ops = ["noop", "read"]
rw_read_din_data = "0"*self.word_size
check = 0
# First cycle idle
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, self.wmask, 0, self.t_current)
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks)
# Write at least once
addr = self.gen_addr()
word = self.gen_data()
comment = self.gen_cycle_comment("write", word, addr, self.wmask, 0, self.t_current)
self.add_write(comment, addr, word, self.wmask, 0)
self.stored_words[addr] = word
# First cycle idle is always an idle cycle
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, "0"*self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
# Read at least once. For multiport, it is important that one read cycle uses all RW and R port to read from the same address simultaniously.
# This will test the viablilty of the transistor sizing in the bitcell.
for port in self.all_ports:
if port in self.write_ports:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
else:
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current)
self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port)
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
check += 1
# 1. Write all the write ports first to seed a bunch of locations.
for port in self.write_ports:
addr = self.gen_addr()
word = self.gen_data()
comment = self.gen_cycle_comment("write", word, addr, "1"*self.num_wmasks, port, self.t_current)
self.add_write_one_port(comment, addr, word, "1"*self.num_wmasks, port)
self.stored_words[addr] = word
# All other read-only ports are noops.
for port in self.read_ports:
if port not in self.write_ports:
self.add_noop_one_port(port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# Perform a random sequence of writes and reads on random ports, using random addresses and random words
# and random write masks (if applicable)
# 2. Read at least once. For multiport, it is important that one
# read cycle uses all RW and R port to read from the same
# address simultaniously. This will test the viablilty of the
# transistor sizing in the bitcell.
for port in self.all_ports:
if port in self.write_ports:
self.add_noop_one_port(port)
else:
comment = self.gen_cycle_comment("read", word, addr, "0"*self.num_wmasks, port, self.t_current)
self.add_read_one_port(comment, addr, port)
self.add_read_check(word, port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.check_lengths()
# 3. Perform a random sequence of writes and reads on random
# ports, using random addresses and random words and random
# write masks (if applicable)
for i in range(self.num_cycles):
w_addrs = []
for port in self.all_ports:
@ -132,63 +152,79 @@ class functional(simulation):
op = random.choice(r_ops)
if op == "noop":
addr = "0"*self.addr_size
word = "0"*self.word_size
wmask = "0" * self.num_wmasks
self.add_noop_one_port(addr, word, wmask, port)
self.add_noop_one_port(port)
elif op == "write":
addr = self.gen_addr()
word = self.gen_data()
# two ports cannot write to the same address
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
self.add_noop_one_port(port)
else:
comment = self.gen_cycle_comment("write", word, addr, self.wmask, port, self.t_current)
self.add_write_one_port(comment, addr, word, self.wmask, port)
word = self.gen_data()
comment = self.gen_cycle_comment("write", word, addr, "1"*self.num_wmasks, port, self.t_current)
self.add_write_one_port(comment, addr, word, "1"*self.num_wmasks, port)
self.stored_words[addr] = word
w_addrs.append(addr)
elif op == "partial_write":
# write only to a word that's been written to
(addr,old_word) = self.get_data()
word = self.gen_data()
wmask = self.gen_wmask()
new_word = word
for bit in range(len(wmask)):
# When the write mask's bits are 0, the old data values should appear in the new word
# as to not overwrite the old values
if wmask[bit] == "0":
lower = bit * self.write_size
upper = lower + self.write_size - 1
new_word = new_word[:lower] + old_word[lower:upper+1] + new_word[upper + 1:]
# two ports cannot write to the same address
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
self.add_noop_one_port(port)
else:
word = self.gen_data()
wmask = self.gen_wmask()
new_word = self.gen_masked_data(old_word, word, wmask)
comment = self.gen_cycle_comment("partial_write", word, addr, wmask, port, self.t_current)
self.add_write_one_port(comment, addr, word, wmask, port)
self.stored_words[addr] = new_word
w_addrs.append(addr)
else:
(addr,word) = random.choice(list(self.stored_words.items()))
# cannot read from an address that is currently being written to
# The write driver is not sized sufficiently to drive through the two
# bitcell access transistors to the read port. So, for now, we do not allow
# a simultaneous write and read to the same address on different ports. This
# could be even more difficult with multiple simultaneous read ports.
if addr in w_addrs:
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port)
self.add_noop_one_port(port)
else:
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current)
self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port)
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check])
check += 1
comment = self.gen_cycle_comment("read", word, addr, "0"*self.num_wmasks, port, self.t_current)
self.add_read_one_port(comment, addr, port)
self.add_read_check(word, port)
self.cycle_times.append(self.t_current)
self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, self.wmask, 0, self.t_current)
self.add_noop_all_ports(comment, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks)
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, "0"*self.num_wmasks, 0, self.t_current)
self.add_noop_all_ports(comment)
def gen_masked_data(self, old_word, word, wmask):
""" Create the masked data word """
# Start with the new word
new_word = word
# When the write mask's bits are 0, the old data values should appear in the new word
# as to not overwrite the old values
for bit in range(len(wmask)):
if wmask[bit] == "0":
lower = bit * self.write_size
upper = lower + self.write_size - 1
new_word = new_word[:lower] + old_word[lower:upper+1] + new_word[upper + 1:]
return new_word
def add_read_check(self, word, port):
""" Add to the check array to ensure a read works. """
try:
self.check
except:
self.check = 0
self.read_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, self.check])
self.check += 1
def read_stim_results(self):
# Extrat dout values from spice timing.lis
for (word, dout_port, eo_period, check) in self.write_check:
# Extract dout values from spice timing.lis
for (word, dout_port, eo_period, check) in self.read_check:
sp_read_value = ""
for bit in range(self.word_size):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check))
@ -205,17 +241,17 @@ class functional(simulation):
self.v_high)
return (0, error)
self.read_check.append([sp_read_value, dout_port, eo_period, check])
self.read_results.append([sp_read_value, dout_port, eo_period, check])
return (1, "SUCCESS")
def check_stim_results(self):
for i in range(len(self.write_check)):
if self.write_check[i][0] != self.read_check[i][0]:
error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_check[i][1],
for i in range(len(self.read_check)):
if self.read_check[i][0] != self.read_results[i][0]:
error = "FAILED: {0} value {1} does not match written value {2} read during cycle {3} at time {4}n".format(self.read_results[i][1],
self.read_results[i][0],
self.read_check[i][0],
self.write_check[i][0],
int((self.read_check[i][2]-self.period)/self.period),
self.read_check[i][2])
int((self.read_results[i][2]-self.period)/self.period),
self.read_results[i][2])
return(0, error)
return(1, "SUCCESS")
@ -359,7 +395,7 @@ class functional(simulation):
# Generate dout value measurements
self.sf.write("\n * Generation of dout measurements\n")
for (word, dout_port, eo_period, check) in self.write_check:
for (word, dout_port, eo_period, check) in self.read_check:
t_intital = eo_period - 0.01*self.period
t_final = eo_period + 0.01*self.period
for bit in range(self.word_size):

View File

@ -60,8 +60,10 @@ class simulation():
port_info=(len(self.all_ports),self.write_ports,self.read_ports),
abits=self.addr_size,
dbits=self.word_size)
debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \
do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,self.pins))
debug.check(len(self.sram.pins) == len(self.pins),
"Number of pins generated for characterization \
do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,
self.pins))
#This is TODO once multiport control has been finalized.
#self.control_name = "CSB"
@ -71,13 +73,18 @@ class simulation():
self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port
self.csb_values = [[] for port in self.all_ports]
self.web_values = [[] for port in self.readwrite_ports]
# Three dimensional list to handle each addr and data bits for wach port over the number of checks
self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports]
self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports]
self.wmask_values = [[[] for bit in range(self.num_wmasks)] for port in self.write_ports]
self.csb_values = {port:[] for port in self.all_ports}
self.web_values = {port:[] for port in self.readwrite_ports}
# Raw values added as a bit vector
self.addr_value = {port:[] for port in self.all_ports}
self.data_value = {port:[] for port in self.write_ports}
self.wmask_value = {port:[] for port in self.write_ports}
# Three dimensional list to handle each addr and data bits for each port over the number of checks
self.addr_values = {port:[[] for bit in range(self.addr_size)] for port in self.all_ports}
self.data_values = {port:[[] for bit in range(self.word_size)] for port in self.write_ports}
self.wmask_values = {port:[[] for bit in range(self.num_wmasks)] for port in self.write_ports}
# For generating comments in SPICE stimulus
self.cycle_comments = []
@ -105,7 +112,8 @@ class simulation():
def add_data(self, data, port):
""" Add the array of data values """
debug.check(len(data)==self.word_size, "Invalid data word size.")
self.data_value[port].append(data)
bit = self.word_size - 1
for c in data:
if c=="0":
@ -116,10 +124,12 @@ class simulation():
debug.error("Non-binary data string",1)
bit -= 1
def add_address(self, address, port):
""" Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.")
self.addr_value[port].append(address)
bit = self.addr_size - 1
for c in address:
if c=="0":
@ -130,10 +140,12 @@ class simulation():
debug.error("Non-binary address string",1)
bit -= 1
def add_wmask(self, wmask, port):
""" Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
self.wmask_value[port].append(wmask)
bit = self.num_wmasks - 1
for c in wmask:
if c == "0":
@ -143,10 +155,13 @@ class simulation():
else:
debug.error("Non-binary wmask string", 1)
bit -= 1
def add_write(self, comment, address, data, wmask, port):
""" Add the control values for a write cycle. """
debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
debug.check(port in self.write_ports,
"Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port,
self.write_ports))
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment)
@ -159,16 +174,16 @@ class simulation():
self.add_address(address,port)
self.add_wmask(wmask,port)
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
noop_data = "0"*self.word_size
#Add noops to all other ports.
for unselected_port in self.all_ports:
if unselected_port != port:
self.add_noop_one_port(address, noop_data, wmask, unselected_port)
self.add_noop_one_port(unselected_port)
def add_read(self, comment, address, din_data, wmask, port):
def add_read(self, comment, address, port):
""" Add the control values for a read cycle. """
debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports))
debug.check(port in self.read_ports,
"Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port,
self.read_ports))
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment)
@ -176,21 +191,26 @@ class simulation():
self.cycle_times.append(self.t_current)
self.t_current += self.period
self.add_control_one_port(port, "read")
#If the port is also a readwrite then add data.
if port in self.write_ports:
self.add_data(din_data,port)
self.add_wmask(wmask,port)
self.add_address(address, port)
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port
noop_data = "0"*self.word_size
# If the port is also a readwrite then add
# the same value as previous cycle
if port in self.write_ports:
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*self.word_size, port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
#Add noops to all other ports.
for unselected_port in self.all_ports:
if unselected_port != port:
self.add_noop_one_port(address, noop_data, wmask, unselected_port)
self.add_noop_one_port(unselected_port)
def add_noop_all_ports(self, comment, address, data, wmask):
def add_noop_all_ports(self, comment):
""" Add the control values for a noop to all ports. """
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
@ -200,39 +220,64 @@ class simulation():
self.t_current += self.period
for port in self.all_ports:
self.add_noop_one_port(address, data, wmask, port)
self.add_noop_one_port(port)
def add_write_one_port(self, comment, address, data, wmask, port):
""" Add the control values for a write cycle. Does not increment the period. """
debug.check(port in self.write_ports, "Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port, self.write_ports))
debug.check(port in self.write_ports,
"Cannot add write cycle to a read port. Port {0}, Write Ports {1}".format(port,
self.write_ports))
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "write")
self.add_data(data,port)
self.add_address(address,port)
self.add_wmask(wmask,port)
self.add_data(data, port)
self.add_address(address, port)
self.add_wmask(wmask, port)
def add_read_one_port(self, comment, address, din_data, wmask, port):
def add_read_one_port(self, comment, address, port):
""" Add the control values for a read cycle. Does not increment the period. """
debug.check(port in self.read_ports, "Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port, self.read_ports))
debug.check(port in self.read_ports,
"Cannot add read cycle to a write port. Port {0}, Read Ports {1}".format(port,
self.read_ports))
debug.info(2, comment)
self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "read")
#If the port is also a readwrite then add data.
if port in self.write_ports:
self.add_data(din_data,port)
self.add_wmask(wmask,port)
self.add_address(address, port)
# If the port is also a readwrite then add
# the same value as previous cycle
if port in self.write_ports:
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*self.word_size, port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
def add_noop_one_port(self, address, data, wmask, port):
def add_noop_one_port(self, port):
""" Add the control values for a noop to a single port. Does not increment the period. """
self.add_control_one_port(port, "noop")
try:
self.add_address(self.addr_value[port][-1], port)
except:
self.add_address("0"*self.addr_size, port)
# If the port is also a readwrite then add
# the same value as previous cycle
if port in self.write_ports:
self.add_data(data,port)
self.add_wmask(wmask,port)
self.add_address(address, port)
try:
self.add_data(self.data_value[port][-1], port)
except:
self.add_data("0"*self.word_size, port)
try:
self.add_wmask(self.wmask_value[port][-1], port)
except:
self.add_wmask("0"*self.num_wmasks, port)
def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file"""
@ -240,16 +285,16 @@ class simulation():
time = "{0:.2f} ns:".format(self.t_current)
time_spacing = len(time)+6
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
port,
time,
time_spacing,
comment))
port,
time,
time_spacing,
comment))
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop":
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
t_current,
t_current+self.period)
t_current,
t_current+self.period)
elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr,

View File

@ -96,22 +96,6 @@ class stimuli():
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name))
def inst_buffer(self, buffer_name, signal_list):
""" Adds buffers to each top level signal that is in signal_list (only for sim purposes) """
for signal in signal_list:
self.sf.write("X{0}_buffer {0} {0}_buf {1} {2} test_{3}\n".format(signal,
"test"+self.vdd_name,
"test"+self.gnd_name,
buffer_name))
def inst_inverter(self, signal_list):
""" Adds inv for each signal that needs its inverted version (only for sim purposes) """
for signal in signal_list:
self.sf.write("X{0}_inv {0} {0}_inv {1} {2} test_inv\n".format(signal,
"test"+self.vdd_name,
"test"+self.gnd_name))
def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
"""
@ -276,9 +260,6 @@ class stimuli():
""" Writes supply voltage statements """
gnd_node_name = "0"
self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage))
# This is for the test power supply
self.sf.write("V{0} {0} {1} {2}\n".format("test"+self.vdd_name, gnd_node_name, self.voltage))
self.sf.write("V{0} {0} {1} {2}\n".format("test"+self.gnd_name, gnd_node_name, 0.0))
#Adding a commented out supply for simulators where gnd and 0 are not global grounds.
self.sf.write("\n*Nodes gnd and 0 are the same global ground node in ngspice/hspice/xa. Otherwise, this source may be needed.\n")

View File

@ -94,7 +94,7 @@ def info(lev, str):
frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0])
# classname = frm.f_globals['__name__']
if mod.__name__ == None:
if mod.__name__ is None:
class_name = ""
else:
class_name = mod.__name__

View File

@ -3,11 +3,11 @@ num_words = 128
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 5.0 ]
temperatures = [ 25 ]
supply_voltages = [5.0]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size, num_words, tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_1rw_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_1w_1r_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -10,5 +10,7 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)

View File

@ -10,7 +10,9 @@ route_supplies = True
check_lvsdrc = True
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -7,7 +7,9 @@ supply_voltages = [ 5.0 ]
temperatures = [ 25 ]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -3,11 +3,13 @@ num_words = 256
tech_name = "scn4m_subm"
process_corners = ["TT"]
supply_voltages = [ 3.3 ]
temperatures = [ 25 ]
supply_voltages = [3.3]
temperatures = [25]
output_path = "temp"
output_name = "sram_{0}_{1}_{2}".format(word_size,num_words,tech_name)
output_name = "sram_{0}_{1}_{2}".format(word_size,
num_words,
tech_name)
drc_name = "magic"
lvs_name = "netgen"

View File

@ -13,10 +13,7 @@ created without re-running the entire process. Right now, it assumes the nominal
corner, but should probably be extended.
"""
import sys,os
import datetime
import re
import importlib
import sys
from globals import *
(OPTS, args) = parse_args()

View File

@ -42,7 +42,7 @@ class control_logic(design.design):
self.enable_delay_chain_resizing = False
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
self.wl_stage_efforts = None
self.sen_stage_efforts = None
@ -201,7 +201,7 @@ class control_logic(design.design):
def get_heuristic_delay_chain_size(self):
"""Use a basic heuristic to determine the size of the delay chain used for the Sense Amp Enable """
#FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why?
# FIXME: The minimum was 2 fanout, now it will not pass DRC unless it is 3. Why?
delay_fanout = 3 # This can be anything >=3
# Model poorly captures delay of the column mux. Be pessismistic for column mux
if self.words_per_row >= 2:
@ -209,8 +209,8 @@ class control_logic(design.design):
else:
delay_stages = 2
#Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference
#on certain sram configs.
# Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference
# on certain sram configs.
if self.port_type == "r":
delay_stages+=2
@ -226,7 +226,7 @@ class control_logic(design.design):
def does_sen_rise_fall_timing_match(self):
"""Compare the relative rise/fall delays of the sense amp enable and wordline"""
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
self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall):
return False
@ -236,8 +236,9 @@ class control_logic(design.design):
def does_sen_total_timing_match(self):
"""Compare the total delays of the sense amp enable and wordline"""
self.set_sen_wl_delays()
#The sen delay must always be bigger than than the wl delay. This decides how much larger the sen delay must be before
#a re-size is warranted.
# The sen delay must always be bigger than than the wl
# delay. This decides how much larger the sen delay must be
# before a re-size is warranted.
if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay:
return False
else:
@ -250,14 +251,14 @@ class control_logic(design.design):
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
delay_fanout = 3 # This can be anything >=2
#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
# 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
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")
delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay))
if delay_stages%2 == 1: #force an even number of stages.
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))
return (delay_stages, delay_fanout)
@ -268,16 +269,16 @@ class control_logic(design.design):
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
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
#inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
# 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
required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2)
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
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:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise)
@ -294,8 +295,8 @@ class control_logic(design.design):
fanout_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall
break
#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
# 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
elif stages_fall>stages_rise:
fanout_fall+=1
else:
@ -304,13 +305,13 @@ class control_logic(design.design):
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))
#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)]
return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
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).
return 1
delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay))
@ -421,7 +422,7 @@ class control_logic(design.design):
row += 1
if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row)
row += 1
row += 1
if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row)
row += 1
@ -462,6 +463,7 @@ class control_logic(design.design):
""" Create the replica bitline """
self.delay_inst=self.add_inst(name="delay_chain",
mod=self.delay_chain)
# rbl_bl_delay is asserted (1) when the bitline has been discharged
self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self,row):
@ -612,6 +614,8 @@ class control_logic(design.design):
def create_pen_row(self):
self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar",
mod=self.nand2)
# We use the rbl_bl_delay here to ensure that the p_en is only asserted when the
# bitlines have already been discharged. Otherwise, it is a combination loop.
self.connect_inst(["gated_clk_buf", "rbl_bl_delay", "p_en_bar_unbuf", "vdd", "gnd"])
self.p_en_bar_driver_inst=self.add_inst(name="buf_p_en_bar",
@ -646,6 +650,9 @@ class control_logic(design.design):
# GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
mod=self.sen_and3)
# s_en is asserted in the second half of the cycle during a read.
# we also must wait until the bitline has been discharged enough for proper sensing
# hence we use rbl_bl_delay as well.
self.connect_inst(["rbl_bl_delay", "gated_clk_bar", input_name, "s_en", "vdd", "gnd"])
@ -669,7 +676,6 @@ class control_logic(design.design):
self.connect_output(self.s_en_gate_inst, "Z", "s_en")
def create_rbl_delay_row(self):
self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv",
@ -696,7 +702,8 @@ class control_logic(design.design):
rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
def create_wen_row(self):
# input: we (or cs) output: w_en
@ -709,6 +716,7 @@ class control_logic(design.design):
# GATE THE W_EN
self.w_en_gate_inst = self.add_inst(name="w_en_and",
mod=self.wen_and)
# 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"])

View File

@ -16,34 +16,32 @@ a LEF (.lef) file for preliminary P&R (real one should be from layout)
a Liberty (.lib) file for timing analysis/optimization
"""
import sys,os
import sys
import datetime
import re
import importlib
from globals import *
import globals as g
(OPTS, args) = parse_args()
(OPTS, args) = g.parse_args()
# Check that we are left with a single configuration file as argument.
if len(args) != 1:
print(USAGE)
print(g.USAGE)
sys.exit(2)
# These depend on arguments, so don't load them until now.
import debug
init_openram(config_file=args[0], is_unit_test=False)
g.init_openram(config_file=args[0], is_unit_test=False)
# Only print banner here so it's not in unit tests
print_banner()
g.print_banner()
# Keep track of running stats
start_time = datetime.datetime.now()
print_time("Start",start_time)
g.print_time("Start", start_time)
# Output info about this run
report_status()
g.report_status()
from sram_config import sram_config
@ -54,15 +52,16 @@ c = sram_config(word_size=OPTS.word_size,
write_size=OPTS.write_size)
debug.print_raw("Words per row: {}".format(c.words_per_row))
#from parser import *
output_extensions = ["sp","v","lib","py","html","log"]
output_extensions = ["sp", "v", "lib", "py", "html", "log"]
# Only output lef/gds if back-end
if not OPTS.netlist_only:
output_extensions.extend(["lef","gds"])
output_extensions.extend(["lef", "gds"])
output_files = ["{0}{1}.{2}".format(OPTS.output_path,OPTS.output_name,x) for x in output_extensions]
output_files = ["{0}{1}.{2}".format(OPTS.output_path,
OPTS.output_name, x)
for x in output_extensions]
debug.print_raw("Output files are: ")
for path in output_files:
for path in output_files:
debug.print_raw(path)
@ -74,7 +73,7 @@ s = sram(sram_config=c,
s.save()
# Delete temp files etc.
end_openram()
print_time("End",datetime.datetime.now(), start_time)
g.end_openram()
g.print_time("End", datetime.datetime.now(), start_time)

View File

@ -6,13 +6,13 @@
# All rights reserved.
#
import optparse
import getpass
import getpass
import os
#import sram_config
class options(optparse.Values):
"""
Class for holding all of the OpenRAM options. All of these options can be over-riden in a configuration file
Class for holding all of the OpenRAM options. All
of these options can be over-riden in a configuration file
that is the sole required command-line positional argument for openram.py.
"""
@ -39,15 +39,16 @@ class options(optparse.Values):
process_corners = ""
# Size parameters must be specified by user in config file.
#num_words = 0
#word_size = 0
# num_words = 0
# word_size = 0
# You can manually specify banks, but it is better to auto-detect it.
num_banks = 1
###################
# Optimization options
###################
rbl_delay_percentage = 0.5 #Approximate percentage of delay compared to bitlines
# Approximate percentage of delay compared to bitlines
rbl_delay_percentage = 0.5
# Allow manual adjustment of the delay chain over automatic
use_tech_delay_chain_size = False
@ -65,7 +66,8 @@ class options(optparse.Values):
openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP"))
except:
# Else use a unique temporary directory
openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),os.getpid())
openram_temp = "/tmp/openram_{0}_{1}_temp/".format(getpass.getuser(),
os.getpid())
# This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc.
debug_level = 0
@ -100,7 +102,8 @@ class options(optparse.Values):
drc_name = ""
lvs_name = ""
pex_name = ""
# The DRC/LVS/PEX executable being used which is derived from the user PATH.
# The DRC/LVS/PEX executable being used
# which is derived from the user PATH.
drc_exe = None
lvs_exe = None
pex_exe = None
@ -113,15 +116,14 @@ class options(optparse.Values):
output_path = "."
# Define the output file base name
output_name = ""
# Use analytical delay models by default rather than (slow) characterization
# Use analytical delay models by default
# rather than (slow) characterization
analytical_delay = True
# Purge the temp directory after a successful run (doesn't purge on errors, anyhow)
# Purge the temp directory after a successful
# run (doesn't purge on errors, anyhow)
purge_temp = True
###################
# These are the default modules that can be over-riden
###################
bank_select = "bank_select"
bitcell_array = "bitcell_array"
bitcell = "bitcell"

View File

@ -6,16 +6,14 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pand2(pgate.pgate):
"""
This is a simple buffer used for driving loads.
This is a simple buffer used for driving loads.
"""
def __init__(self, name, size=1, height=None):
debug.info(1, "Creating pnand2 {}".format(name))
@ -23,7 +21,7 @@ class pand2(pgate.pgate):
self.size = size
# Creates the netlist and layout
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
@ -33,10 +31,13 @@ class pand2(pgate.pgate):
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand2",height=self.height)
self.nand = factory.create(module_type="pnand2", height=self.height)
self.add_mod(self.nand)
self.inv = factory.create(module_type="pdriver", neg_polarity=True, fanout=3*self.size, height=self.height)
self.inv = factory.create(module_type="pdriver",
neg_polarity=True,
fanout=3*self.size,
height=self.height)
self.add_mod(self.inv)
def create_layout(self):
@ -54,44 +55,44 @@ class pand2(pgate.pgate):
self.add_pin("gnd", "GROUND")
def create_insts(self):
self.nand_inst=self.add_inst(name="pand2_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.nand_inst = self.add_inst(name="pand2_nand",
mod=self.nand)
self.connect_inst(["A", "B", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand2_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand2_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0,0))
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid1_point = vector(0.5 * (z1_pin.cx() + a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
self.add_path("metal1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd")
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd")
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -102,7 +103,7 @@ class pand2(pgate.pgate):
width=pin.width(),
height=pin.height())
for pin_name in ["A","B"]:
for pin_name in ["A", "B"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,

View File

@ -6,16 +6,14 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pand3(pgate.pgate):
"""
This is a simple buffer used for driving loads.
This is a simple buffer used for driving loads.
"""
def __init__(self, name, size=1, height=None):
debug.info(1, "Creating pand3 {}".format(name))
@ -23,7 +21,7 @@ class pand3(pgate.pgate):
self.size = size
# Creates the netlist and layout
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
@ -33,10 +31,12 @@ class pand3(pgate.pgate):
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
self.nand = factory.create(module_type="pnand3",height=self.height)
self.nand = factory.create(module_type="pnand3", height=self.height)
self.add_mod(self.nand)
self.inv = factory.create(module_type="pinv", size=self.size, height=self.height)
self.inv = factory.create(module_type="pinv",
size=self.size,
height=self.height)
self.add_mod(self.inv)
def create_layout(self):
@ -55,44 +55,44 @@ class pand3(pgate.pgate):
self.add_pin("gnd", "GROUND")
def create_insts(self):
self.nand_inst=self.add_inst(name="pand3_nand",
mod=self.nand)
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
self.nand_inst = self.add_inst(name="pand3_nand",
mod=self.nand)
self.connect_inst(["A", "B", "C", "zb_int", "vdd", "gnd"])
self.inv_inst=self.add_inst(name="pand3_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.inv_inst = self.add_inst(name="pand3_inv",
mod=self.inv)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add NAND to the right
self.nand_inst.place(offset=vector(0,0))
# Add NAND to the right
self.nand_inst.place(offset=vector(0, 0))
# Add INV to the right
self.inv_inst.place(offset=vector(self.nand_inst.rx(),0))
self.inv_inst.place(offset=vector(self.nand_inst.rx(), 0))
def add_wires(self):
# nand Z to inv A
z1_pin = self.nand_inst.get_pin("Z")
a2_pin = self.inv_inst.get_pin("A")
mid1_point = vector(0.5*(z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid1_point = vector(0.5 * (z1_pin.cx()+a2_pin.cx()), z1_pin.cy())
mid2_point = vector(mid1_point, a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
self.add_path("metal1",
[z1_pin.center(), mid1_point, mid2_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv_inst.get_pin("vdd")
vdd_pin = self.inv_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv_inst.get_pin("gnd")
gnd_pin = self.inv_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -103,20 +103,22 @@ class pand3(pgate.pgate):
width=pin.width(),
height=pin.height())
for pin_name in ["A","B", "C"]:
for pin_name in ["A", "B", "C"]:
pin = self.nand_inst.get_pin(pin_name)
self.add_layout_pin_rect_center(text=pin_name,
layer=pin.layer,
offset=pin.center(),
width=pin.width(),
height=pin.height())
def analytical_delay(self, corner, slew, load=0.0):
""" Calculate the analytical delay of DFF-> INV -> INV """
nand_delay = self.nand.analytical_delay(corner, slew=slew, load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner, slew=nand_delay.slew, load=load)
nand_delay = self.nand.analytical_delay(corner,
slew=slew,
load=self.inv.input_load())
inv_delay = self.inv.analytical_delay(corner,
slew=nand_delay.slew,
load=load)
return nand_delay + inv_delay
def get_stage_efforts(self, external_cout, inp_is_rise=False):

View File

@ -6,20 +6,18 @@
# All rights reserved.
#
import debug
from tech import drc
from math import log
from vector import vector
from globals import OPTS
import pgate
from sram_factory import factory
class pbuf(pgate.pgate):
"""
This is a simple buffer used for driving loads.
This is a simple buffer used for driving loads.
"""
def __init__(self, name, size=4, height=None):
debug.info(1, "creating {0} with size of {1}".format(name,size))
debug.info(1, "creating {0} with size of {1}".format(name, size))
self.add_comment("size: {}".format(size))
self.stage_effort = 4
@ -29,7 +27,6 @@ class pbuf(pgate.pgate):
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
self.create_modules()
@ -49,53 +46,54 @@ class pbuf(pgate.pgate):
def create_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.size/self.stage_effort))
self.inv1 = factory.create(module_type="pinv", size=input_size, height=self.height)
input_size = max(1, int(self.size / self.stage_effort))
self.inv1 = factory.create(module_type="pinv",
size=input_size,
height=self.height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.height)
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.height)
self.add_mod(self.inv2)
def create_insts(self):
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv1)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_insts(self):
# Add INV1 to the right
self.inv1_inst.place(vector(0,0))
# Add INV1 to the right
self.inv1_inst.place(vector(0, 0))
# 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 add_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd")
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd")
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())

View File

@ -7,28 +7,27 @@
#
import debug
import pgate
import math
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class pdriver(pgate.pgate):
"""
This instantiates an even or odd number of inverters sized for driving a load.
This instantiates an even or odd number of inverters
sized for driving a load.
"""
def __init__(self, name, neg_polarity=False, fanout=0, size_list=None, height=None):
debug.info(1, "creating pdriver {}".format(name))
self.stage_effort = 3
self.height = height
self.height = height
self.neg_polarity = neg_polarity
self.size_list = size_list
self.fanout = fanout
if size_list == None and self.fanout == 0:
if not size_list and self.fanout == 0:
debug.error("Either fanout or size list must be specified.", -1)
if self.size_list and self.fanout != 0:
debug.error("Cannot specify both size_list and fanout.", -1)
@ -36,34 +35,33 @@ class pdriver(pgate.pgate):
debug.error("Cannot specify both size_list and neg_polarity.", -1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
pgate.pgate.__init__(self, name, height)
def compute_sizes(self):
# size_list specified
if self.size_list:
self.num_stages = len(self.size_list)
else:
# Find the optimal number of stages for the given effort
self.num_stages = max(1,int(round(self.fanout**(1/self.stage_effort))))
self.num_stages = max(1,
int(round(self.fanout ** (1 / self.stage_effort))))
# Increase the number of stages if we need to fix polarity
if self.neg_polarity and (self.num_stages%2==0):
if self.neg_polarity and (self.num_stages % 2 == 0):
self.num_stages += 1
elif not self.neg_polarity and (self.num_stages%2):
elif not self.neg_polarity and (self.num_stages % 2):
self.num_stages += 1
self.size_list = []
# compute sizes backwards from the fanout
fanout_prev = self.fanout
for x in range(self.num_stages):
fanout_prev = max(round(fanout_prev/self.stage_effort),1)
fanout_prev = max(round(fanout_prev / self.stage_effort), 1)
self.size_list.append(fanout_prev)
# reverse the sizes to be from input to output
self.size_list.reverse()
def create_netlist(self):
self.compute_sizes()
self.add_comment("sizes: {}".format(str(self.size_list)))
@ -79,29 +77,30 @@ class pdriver(pgate.pgate):
self.width = self.inv_inst_list[-1].rx()
self.height = self.inv_inst_list[0].height
def add_pins(self):
self.add_pin("A", "INPUT")
self.add_pin("Z", "OUTPUT")
self.add_pin("vdd", "POWER")
self.add_pin("gnd", "GROUND")
def add_modules(self):
def add_modules(self):
self.inv_list = []
for size in self.size_list:
temp_inv = factory.create(module_type="pinv", size=size, height=self.height)
temp_inv = factory.create(module_type="pinv",
size=size,
height=self.height)
self.inv_list.append(temp_inv)
self.add_mod(temp_inv)
def create_insts(self):
self.inv_inst_list = []
for x in range(1,self.num_stages+1):
for x in range(1, self.num_stages + 1):
# Create first inverter
if x == 1:
zbx_int = "Zb{}_int".format(x);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbx_int = "Zb{}_int".format(x)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
if self.num_stages == 1:
self.connect_inst(["A", "Z", "vdd", "gnd"])
else:
@ -109,70 +108,72 @@ class pdriver(pgate.pgate):
# Create last inverter
elif x == self.num_stages:
zbn_int = "Zb{}_int".format(x-1);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbn_int = "Zb{}_int".format(x - 1)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbn_int, "Z", "vdd", "gnd"])
# Create middle inverters
else:
zbx_int = "Zb{}_int".format(x-1);
zbn_int = "Zb{}_int".format(x);
self.inv_inst_list.append(self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x-1]))
zbx_int = "Zb{}_int".format(x - 1)
zbn_int = "Zb{}_int".format(x)
inst = self.add_inst(name="buf_inv{}".format(x),
mod=self.inv_list[x - 1])
self.inv_inst_list.append(inst)
self.connect_inst([zbx_int, zbn_int, "vdd", "gnd"])
def place_modules(self):
# Add the first inverter at the origin
self.inv_inst_list[0].place(vector(0,0))
self.inv_inst_list[0].place(vector(0, 0))
# Add inverters to the right of the previous inverter
for x in range(1,len(self.inv_inst_list)):
self.inv_inst_list[x].place(vector(self.inv_inst_list[x-1].rx(),0))
for x in range(1, len(self.inv_inst_list)):
loc = vector(self.inv_inst_list[x - 1].rx(), 0)
self.inv_inst_list[x].place(loc)
def route_wires(self):
z_inst_list = []
a_inst_list = []
# inv_current Z to inv_next A
for x in range(0,len(self.inv_inst_list)-1):
for x in range(0, len(self.inv_inst_list) - 1):
z_inst_list.append(self.inv_inst_list[x].get_pin("Z"))
a_inst_list.append(self.inv_inst_list[x+1].get_pin("A"))
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
self.add_path("metal1", [z_inst_list[x].center(), mid_point, a_inst_list[x].center()])
a_inst_list.append(self.inv_inst_list[x + 1].get_pin("A"))
mid_point = vector(z_inst_list[x].cx(), a_inst_list[x].cy())
self.add_path("metal1",
[z_inst_list[x].center(), mid_point,
a_inst_list[x].center()])
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv_inst_list[0].get_pin("vdd")
vdd_pin = self.inv_inst_list[0].get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv_inst_list[0].get_pin("gnd")
gnd_pin = self.inv_inst_list[0].get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
z_pin = self.inv_inst_list[len(self.inv_inst_list)-1].get_pin("Z")
z_pin = self.inv_inst_list[len(self.inv_inst_list) - 1].get_pin("Z")
self.add_layout_pin_rect_center(text="Z",
layer=z_pin.layer,
offset=z_pin.center(),
width = z_pin.width(),
height = z_pin.height())
width=z_pin.width(),
height=z_pin.height())
a_pin = self.inv_inst_list[0].get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer=a_pin.layer,
offset=a_pin.center(),
width = a_pin.width(),
height = a_pin.height())
width=a_pin.width(),
height=a_pin.height())
def get_sizes(self):
""" Return the relative sizes of the buffers """
@ -181,14 +182,14 @@ class pdriver(pgate.pgate):
def get_stage_efforts(self, external_cout, inp_is_rise=False):
""" Get the stage efforts of the A -> Z path """
cout_list = []
for prev_inv,inv in zip(self.inv_list, self.inv_list[1:]):
for prev_inv, inv in zip(self.inv_list, self.inv_list[1:]):
cout_list.append(inv.get_cin())
cout_list.append(external_cout)
stage_effort_list = []
last_inp_is_rise = inp_is_rise
for inv,cout in zip(self.inv_list,cout_list):
for inv, cout in zip(self.inv_list, cout_list):
stage = inv.get_stage_effort(cout, last_inp_is_rise)
stage_effort_list.append(stage)
last_inp_is_rise = stage.is_rise

View File

@ -8,14 +8,16 @@
import contact
import design
import debug
from tech import drc, parameter, spice
from tech import drc
from vector import vector
from globals import OPTS
from sram_factory import factory
class pgate(design.design):
"""
This is a module that implements some shared functions for parameterized gates.
This is a module that implements some shared
functions for parameterized gates.
"""
def __init__(self, name, height=None):
@ -29,78 +31,85 @@ class pgate(design.design):
self.height = b.height
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.add_boundary()
self.DRC_LVS()
def create_netlist(self):
""" Pure virtual function """
debug.error("Must over-ride create_netlist.",-1)
debug.error("Must over-ride create_netlist.", -1)
def create_layout(self):
""" Pure virtual function """
debug.error("Must over-ride create_layout.",-1)
debug.error("Must over-ride create_layout.", -1)
def connect_pin_to_rail(self,inst,pin,supply):
def connect_pin_to_rail(self, inst, pin, supply):
""" Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin)
supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin):
return
if supply=="gnd":
height=supply_pin.by()-source_pin.by()
elif supply=="vdd":
height=supply_pin.uy()-source_pin.by()
if supply == "gnd":
height = supply_pin.by() - source_pin.by()
elif supply == "vdd":
height = supply_pin.uy() - source_pin.by()
else:
debug.error("Invalid supply name.",-1)
debug.error("Invalid supply name.", -1)
if abs(height)>0:
if abs(height) > 0:
self.add_rect(layer="metal1",
offset=source_pin.ll(),
height=height,
width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False):
""" Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or right of gate. """
"""
Route the input gate to the left side of the cell for access.
Position specifies to place the contact the left, center, or
right of gate.
"""
nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_inst.get_pin("G")
# Check if the gates are aligned and give an error if they aren't!
debug.check(nmos_gate_pin.ll().x==pmos_gate_pin.ll().x, "Connecting unaligned gates not supported.")
debug.check(nmos_gate_pin.ll().x == pmos_gate_pin.ll().x,
"Connecting unaligned gates not supported.")
# Pick point on the left of NMOS and connect down to PMOS
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5*self.poly_width,0)
pmos_gate_pos = vector(nmos_gate_pos.x,pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos])
nmos_gate_pos = nmos_gate_pin.ll() + vector(0.5 * self.poly_width, 0)
pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y)
self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Add the via to the cell midpoint along the gate
left_gate_offset = vector(nmos_gate_pin.lx(),ypos)
left_gate_offset = vector(nmos_gate_pin.lx(), ypos)
# Center is completely symmetric.
# Center is completely symmetric.
if rotate:
contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width
directions = ("H","V")
directions = ("H", "V")
else:
contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height
directions = ("V","H")
directions = ("V", "H")
if position=="center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0)
elif position=="farleft":
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0)
elif position=="left":
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0)
elif position=="right":
contact_offset = left_gate_offset + vector(0.5*contact.width + 0.5*self.poly_width, 0)
if position == "center":
contact_offset = left_gate_offset \
+ vector(0.5 * self.poly_width, 0)
elif position == "farleft":
contact_offset = left_gate_offset \
- vector(0.5 * contact.poly.width, 0)
elif position == "left":
contact_offset = left_gate_offset \
- vector(0.5 * contact_width - 0.5 * self.poly_width, 0)
elif position == "right":
contact_offset = left_gate_offset \
+ vector(0.5 * contact.width + 0.5 * self.poly_width, 0)
else:
debug.error("Invalid contact placement option.", -1)
@ -110,29 +119,26 @@ class pgate(design.design):
offset=contact_offset,
directions=directions)
# self.add_layout_pin_segment_center(text=name,
# layer="metal1",
# start=left_gate_offset.scale(0,1),
# end=left_gate_offset)
self.add_layout_pin_rect_center(text=name,
layer="metal1",
offset=contact_offset,
width=contact_m1_width,
height=contact_m1_height)
# This is to ensure that the contact is connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0)
# This is to ensure that the contact is
# connected to the gate
mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly",
offset=mid_point,
height=contact.poly.first_layer_width,
width=left_gate_offset.x-contact_offset.x)
width=left_gate_offset.x - contact_offset.x)
def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """
# Add a rail width to extend the well to the top of the rail
max_y_offset = self.height + 0.5*self.m1_width
max_y_offset = self.height + 0.5 * self.m1_width
self.nwell_position = middle_position
nwell_height = max_y_offset - middle_position.y
if drc("has_nwell"):
@ -145,8 +151,8 @@ class pgate(design.design):
width=self.well_width,
height=nwell_height)
pwell_position = vector(0,-0.5*self.m1_width)
pwell_height = middle_position.y-pwell_position.y
pwell_position = vector(0, -0.5 * self.m1_width)
pwell_height = middle_position.y - pwell_position.y
if drc("has_pwell"):
self.add_rect(layer="pwell",
offset=pwell_position,
@ -163,38 +169,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge
contact_xoffset = pmos_pos.x + pmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active down from the top of the well
contact_xoffset = pmos_pos.x + pmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active down
# from the top of the well
# OR align the active with the top of PMOS active.
max_y_offset = self.height + 0.5*self.m1_width
max_y_offset = self.height + 0.5 * self.m1_width
contact_yoffset = min(pmos_pos.y + pmos.active_height - pmos.active_contact.first_layer_height,
max_y_offset - pmos.active_contact.first_layer_height/2 - self.well_enclose_active)
max_y_offset - pmos.active_contact.first_layer_height / 2 - self.well_enclose_active)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y
contact_offset += vector(0.5*pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height)
self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H","V"),
implant_type="n",
well_type="n")
contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="n",
well_type="n")
self.add_rect_center(layer="metal1",
offset=contact_offset + vector(0,0.5*(self.height-contact_offset.y)),
offset=contact_offset + vector(0, 0.5 * (self.height-contact_offset.y)),
width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y)
# Now add the full active and implant for the PMOS
#active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=pmos.active_contact.width,
# height=pmos.active_height)
# we need to ensure implants don't overlap and are spaced far enough apart
# we need to ensure implants don't overlap and are
# spaced far enough apart
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = pmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = pmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="nimplant",
# offset=implant_offset,
@ -208,39 +221,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1")
pwell_position = vector(0,-0.5*self.m1_width)
pwell_position = vector(0, -0.5 * self.m1_width)
# To the right a spacing away from the nmos right active edge
contact_xoffset = nmos_pos.x + nmos.active_width + drc("active_to_body_active")
# Must be at least an well enclosure of active up from the bottom of the well
contact_xoffset = nmos_pos.x + nmos.active_width \
+ drc("active_to_body_active")
# Must be at least an well enclosure of active up
# from the bottom of the well
contact_yoffset = max(nmos_pos.y,
self.well_enclose_active - nmos.active_contact.first_layer_height/2)
self.well_enclose_active \
- nmos.active_contact.first_layer_height / 2)
contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact
contact_offset += vector(0.5*nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height)
self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H","V"),
implant_type="p",
well_type="p")
contact_offset += vector(0.5 * nmos.active_contact.first_layer_width,
0.5 * nmos.active_contact.first_layer_height)
self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset,
directions=("H", "V"),
implant_type="p",
well_type="p")
self.add_rect_center(layer="metal1",
offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y)
# Now add the full active and implant for the NMOS
# active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed
# active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active",
# offset=active_offset,
# width=nmos.active_contact.width,
# height=nmos.active_height)
# implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) \
# - vector(0,self.implant_enclose_active)
# implant_width = nmos.active_contact.width \
# + 2*self.implant_enclose_active
# implant_height = nmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="pimplant",
# offset=implant_offset,

View File

@ -16,6 +16,7 @@ from utils import round_to_grid
import logical_effort
from sram_factory import factory
class pinv(pgate.pgate):
"""
Pinv generates gds of a parametrically sized inverter. The
@ -28,12 +29,14 @@ class pinv(pgate.pgate):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True):
debug.info(2, "creating pinv structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pinv structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
self.nmos_size = size
self.pmos_size = beta*size
self.pmos_size = beta * size
self.beta = beta
self.route_output = False
@ -44,7 +47,7 @@ class pinv(pgate.pgate):
self.add_pins()
self.determine_tx_mults()
self.add_ptx()
self.create_ptx()
self.create_ptx()
def create_layout(self):
""" Calls all functions related to the generation of the layout """
@ -54,7 +57,11 @@ class pinv(pgate.pgate):
self.add_well_contacts()
self.extend_wells(self.well_pos)
self.connect_rails()
self.route_input_gate(self.pmos_inst, self.nmos_inst, self.output_pos.y, "A", position="farleft")
self.route_input_gate(self.pmos_inst,
self.nmos_inst,
self.output_pos.y,
"A",
position="farleft")
self.route_outputs()
def add_pins(self):
@ -63,7 +70,6 @@ class pinv(pgate.pgate):
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self):
"""
Determines the number of fingers needed to achieve the size within
@ -73,58 +79,71 @@ class pinv(pgate.pgate):
# This may make the result differ when the layout is created...
if OPTS.netlist_only:
self.tx_mults = 1
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
return
# Do a quick sanity check and bail if unlikely feasible height
# Sanity check. can we make an inverter in the height with minimum tx sizes?
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain)
# Sanity check. can we make an inverter in the height
# with minimum tx sizes?
# Assume we need 3 metal 1 pitches (2 power rails, one
# between the tx for the drain)
# plus the tx height
nmos = factory.create(module_type="ptx", tx_type="nmos")
pmos = factory.create(module_type="ptx", width=drc("minwidth_tx"), tx_type="pmos")
pmos = factory.create(module_type="ptx",
width=drc("minwidth_tx"),
tx_type="pmos")
tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2*drc("poly_to_active"))
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(),0)
contact.poly.width + 2 * drc("poly_to_active"))
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
total_height = tx_height + min_channel + 2*self.top_bottom_space
debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height))
total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height > total_height,
"Cell height {0} too small for simple min height {1}.".format(self.height,
total_height))
# Determine the height left to the transistors to determine the number of fingers
tx_height_available = self.height - min_channel - 2*self.top_bottom_space
# Divide the height in half. Could divide proportional to beta, but this makes
# connecting wells of multiple cells easier.
# Determine the height left to the transistors to determine
# the number of fingers
tx_height_available = self.height - min_channel - 2 * self.top_bottom_space
# Divide the height in half. Could divide proportional to beta,
# but this makes connecting wells of multiple cells easier.
# Subtract the poly space under the rail of the tx
nmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5*drc("poly_to_poly")
nmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
pmos_height_available = 0.5 * tx_height_available - 0.5 * drc("poly_to_poly")
debug.info(2,"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
debug.info(2,
"Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
nmos_height_available,
pmos_height_available))
# Determine the number of mults for each to fit width into available space
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1)
pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1)
# Determine the number of mults for each to fit width
# into available space
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width / nmos_height_available)), 1)
pmos_required_mults = max(int(ceil(self.pmos_width / pmos_height_available)), 1)
# The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it...
# We also need to round the width to the grid or we will end up with LVS property
# mismatch errors when fingers are not a grid length and get rounded in the offset geometry.
# We also need to round the width to the grid or we will end up
# with LVS property mismatch errors when fingers are not a grid
# length and get rounded in the offset geometry.
self.nmos_width = round_to_grid(self.nmos_width / self.tx_mults)
debug.check(self.nmos_width>=drc("minwidth_tx"),"Cannot finger NMOS transistors to fit cell height.")
debug.check(self.nmos_width >= drc("minwidth_tx"),
"Cannot finger NMOS 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.")
debug.check(self.pmos_width >= drc("minwidth_tx"),
"Cannot finger PMOS transistors to fit cell height.")
def setup_layout_constants(self):
"""
@ -136,9 +155,7 @@ class pinv(pgate.pgate):
self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# Height is an input parameter, so it is not recomputed.
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
@ -162,58 +179,57 @@ class pinv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS netlist.
"""
self.pmos_inst=self.add_inst(name="pinv_pmos",
mod=self.pmos)
self.pmos_inst = self.add_inst(name="pinv_pmos",
mod=self.pmos)
self.connect_inst(["Z", "A", "vdd", "vdd"])
self.nmos_inst=self.add_inst(name="pinv_nmos",
mod=self.nmos)
self.nmos_inst = self.add_inst(name="pinv_nmos",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
# place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) \
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_space)
self.pmos_pos = self.pmos.active_offset.scale(1, 0) \
+ vector(0,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos_inst.place(self.pmos_pos)
# place NMOS so that it is half a poly spacing up from the bottom
self.nmos_pos = self.nmos.active_offset.scale(1,0) + vector(0,self.top_bottom_space)
self.nmos_pos = self.nmos.active_offset.scale(1, 0) \
+ vector(0, self.top_bottom_space)
self.nmos_inst.place(self.nmos_pos)
# Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
nmos_drain_pos = self.nmos_inst.get_pin("D").ul()
self.output_pos = vector(0,0.5*(pmos_drain_pos.y+nmos_drain_pos.y))
# This will help with the wells
self.well_pos = vector(0,self.nmos_inst.uy())
self.output_pos = vector(0, 0.5 * (pmos_drain_pos.y + nmos_drain_pos.y))
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
def route_outputs(self):
""" Route the output (drains) together. Optionally, routes output to edge. """
"""
Route the output (drains) together.
Optionally, routes output to edge.
"""
# Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D")
@ -222,14 +238,14 @@ class pinv(pgate.pgate):
# Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos])
self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x,self.output_pos.y)
mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
if self.route_output == True:
if self.route_output:
# This extends the output to the edge of the cell
output_offset = mid_drain_offset.scale(0,1) + vector(self.width,0)
output_offset = mid_drain_offset.scale(0, 1) + vector(self.width, 0)
self.add_layout_pin_segment_center(text="Z",
layer="metal1",
start=mid_drain_offset,
@ -238,8 +254,8 @@ class pinv(pgate.pgate):
# This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z",
layer="metal1",
offset=mid_drain_offset + vector(0.5*self.m1_width,0))
offset=mid_drain_offset \
+ vector(0.5 * self.m1_width, 0))
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -251,9 +267,9 @@ class pinv(pgate.pgate):
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos_inst,"S","gnd")
self.connect_pin_to_rail(self.nmos_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos_inst, "S", "vdd")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
@ -268,27 +284,35 @@ class pinv(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.5
return transition_prob*(c_load + c_para)
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor"""
"""
Return the capacitance of the gate connection in generic capacitive
units relative to the minimum width of a transistor
"""
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 1
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -7,12 +7,10 @@
#
import debug
import pgate
from tech import drc
from math import log
from vector import vector
from globals import OPTS
from sram_factory import factory
class pinvbuf(pgate.pgate):
"""
This is a simple inverter/buffer used for driving loads. It is
@ -31,11 +29,10 @@ class pinvbuf(pgate.pgate):
# The pinvbuf has a FO of 2 for the first stage, so the second stage
# should be sized "half" to prevent loading of the first stage
self.size = size
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
def create_netlist(self):
self.add_pins()
@ -44,8 +41,8 @@ class pinvbuf(pgate.pgate):
def create_layout(self):
self.width = 2*self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height
self.width = 2 * self.inv1.width + self.inv2.width
self.height = 2 * self.inv1.height
self.place_modules()
self.route_wires()
@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate):
self.offset_all_coordinates()
def add_pins(self):
self.add_pin("A")
self.add_pin("Zb")
@ -64,96 +60,100 @@ class pinvbuf(pgate.pgate):
def add_modules(self):
# Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.predriver_size/self.stage_effort))
self.inv = factory.create(module_type="pinv", size=input_size, height=self.row_height)
input_size = max(1, int(self.predriver_size / self.stage_effort))
self.inv = factory.create(module_type="pinv",
size=input_size,
height=self.row_height)
self.add_mod(self.inv)
self.inv1 = factory.create(module_type="pinv", size=self.predriver_size, height=self.row_height)
self.inv1 = factory.create(module_type="pinv",
size=self.predriver_size,
height=self.row_height)
self.add_mod(self.inv1)
self.inv2 = factory.create(module_type="pinv", size=self.size, height=self.row_height)
self.inv2 = factory.create(module_type="pinv",
size=self.size,
height=self.row_height)
self.add_mod(self.inv2)
def create_insts(self):
# Create INV1 (capacitance shield)
self.inv1_inst=self.add_inst(name="buf_inv1",
mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst=self.add_inst(name="buf_inv2",
mod=self.inv1)
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
mod=self.inv1)
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv3_inst=self.add_inst(name="buf_inv3",
mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv3_inst = self.add_inst(name="buf_inv3",
mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv4_inst=self.add_inst(name="buf_inv4",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
self.inv4_inst = self.add_inst(name="buf_inv4",
mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_modules(self):
# Add INV1 to the left (capacitance shield)
self.inv1_inst.place(vector(0,0))
self.inv1_inst.place(vector(0, 0))
# Add INV2 to the right of INV1
self.inv2_inst.place(vector(self.inv1_inst.rx(),0))
self.inv2_inst.place(vector(self.inv1_inst.rx(), 0))
# Add INV3 to the right of INV2
self.inv3_inst.place(vector(self.inv2_inst.rx(),0))
self.inv3_inst.place(vector(self.inv2_inst.rx(), 0))
# Add INV4 flipped to the bottom aligned with INV2
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height),
mirror = "MX")
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),
2 * self.inv2.height),
mirror="MX")
def route_wires(self):
# inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy())
mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
# inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z")
a3_pin = self.inv3_inst.get_pin("A")
mid_point = vector(z2_pin.cx(), a3_pin.cy())
mid_point = vector(z2_pin.cx(), a3_pin.cy())
self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()])
# inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1","via1","metal2"),
mid_point = vector(z1_pin.cx(), a4_pin.cy())
self.add_wire(("metal1", "via1", "metal2"),
[z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z1_pin.center())
def add_layout_pins(self):
# Continous vdd rail along with label.
vdd_pin=self.inv1_inst.get_pin("vdd")
vdd_pin = self.inv1_inst.get_pin("vdd")
self.add_layout_pin(text="vdd",
layer="metal1",
offset=vdd_pin.ll().scale(0,1),
offset=vdd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
# Continous vdd rail along with label.
gnd_pin=self.inv4_inst.get_pin("gnd")
gnd_pin = self.inv4_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=gnd_pin.height())
# Continous gnd rail along with label.
gnd_pin=self.inv1_inst.get_pin("gnd")
gnd_pin = self.inv1_inst.get_pin("gnd")
self.add_layout_pin(text="gnd",
layer="metal1",
offset=gnd_pin.ll().scale(0,1),
offset=gnd_pin.ll().scale(0, 1),
width=self.width,
height=vdd_pin.height())
@ -161,22 +161,21 @@ class pinvbuf(pgate.pgate):
self.add_layout_pin_rect_center(text="Z",
layer="metal2",
offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Zb",
layer="metal2",
offset=zb_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A",
layer="metal2",
offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=a_pin.center())
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False):
@ -194,7 +193,8 @@ class pinvbuf(pgate.pgate):
def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the clk -> clk_buf path"""
#After (almost) every stage, the direction of the signal inverts.
# After (almost) every stage, the direction of the signal inverts.
stage_effort_list = []
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,17 +22,19 @@ class pnand2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """
debug.info(2, "creating pnand2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.size = size
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_size = 2 * size
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand2 is only supported now.")
debug.check(size == 1, "Size 1 pnand2 is only supported now.")
self.tx_mults = 1
# Creates the netlist and layout
@ -61,7 +63,6 @@ class pnand2(pgate.pgate):
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list)
def add_ptx(self):
""" Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx",
@ -90,113 +91,126 @@ class pnand2(pgate.pgate):
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# Compute the other pmos2 location,
# but determining offset to overlap the
# source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + contact.active.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width + contact.active.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5*self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Add PMOS and NMOS to the netlist.
"""
self.pmos1_inst=self.add_inst(name="pnand2_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos)
self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand2_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand2_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos)
self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
"""
Add n/p well taps to the layout and connect to supplies
AFTER the wells are created
"""
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd")
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
def route_inputs(self):
""" Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + 0.5 * self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
# PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center()
# NMOS2 drain
@ -204,24 +218,26 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center()
# Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch,self.inputA_yoffset)
out_offset = vector(nmos_pin.center().x + self.m1_pitch,
self.inputA_yoffset)
# 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)
mid2_offset = vector(out_offset.x, bottom_pin_offset.y)
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center(),
directions=("V","H"))
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(),
directions=("V","H"))
directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[top_pin_offset, mid1_offset, out_offset, mid2_offset, bottom_pin_offset])
self.add_path("metal2",
[top_pin_offset, mid1_offset, out_offset,
mid2_offset, bottom_pin_offset])
# This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z",
@ -243,21 +259,32 @@ class pnand2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob*(c_load + c_para)
return transition_prob * (c_load + c_para)
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size+self.pmos_size
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 2
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -10,10 +10,10 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnand3(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nand.
@ -22,24 +22,25 @@ class pnand3(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """
debug.info(2, "creating pnand3 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnand3 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell...
# If we relax this, we could size this better.
self.size = size
self.nmos_size = 2*size
self.pmos_size = parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.nmos_size = 2 * size
self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnand3 is only supported now.")
debug.check(size == 1,"Size 1 pnand3 is only supported now.")
self.tx_mults = 1
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def add_pins(self):
""" Adds pins for spice netlist """
@ -90,41 +91,44 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \
self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \
- self.overlap_offset.x
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This will help with the wells and the input/output placement
self.output_pos = vector(0,0.5*self.height)
self.output_pos = vector(0, 0.5*self.height)
# This is the extra space needed to ensure DRC rules to the active contacts
# This is the extra space needed to ensure DRC rules
# to the active contacts
nmos = factory.create(module_type="ptx", tx_type="nmos")
extra_contact_space = max(-nmos.get_pin("D").by(),0)
extra_contact_space = max(-nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space \
+ extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS in the netlist.
"""
self.pmos1_inst=self.add_inst(name="pnand3_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
@ -135,27 +139,27 @@ class pnand3(pgate.pgate):
mod=self.pmos)
self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand3_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand3_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos)
self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst=self.add_inst(name="pnand3_nmos3",
mod=self.nmos)
self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos)
self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self):
"""
Place the PMOS and NMOS in the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
Place the PMOS and NMOS in the layout at the upper-most
and lowest position to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.overlap_offset
@ -165,7 +169,8 @@ class pnand3(pgate.pgate):
self.pmos3_inst.place(self.pmos3_pos)
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space)
nmos1_pos = vector(self.pmos.active_offset.x,
self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.overlap_offset
@ -175,7 +180,7 @@ class pnand3(pgate.pgate):
self.nmos3_inst.place(self.nmos3_pos)
# This should be placed at the top of the NMOS well
self.well_pos = vector(0,self.nmos1_inst.uy())
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
@ -183,42 +188,53 @@ class pnand3(pgate.pgate):
self.add_nwell_contact(self.pmos, self.pmos3_pos)
self.add_pwell_contact(self.nmos, self.nmos3_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
self.connect_pin_to_rail(self.pmos2_inst,"D","vdd")
self.connect_pin_to_rail(self.pmos2_inst, "D", "vdd")
def route_inputs(self):
""" Route the A and B inputs """
# wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width,
self.m1_space + 0.5*contact.poly.width + 0.5*self.m1_width)
metal_spacing = max(self.m1_space + self.m1_width,
self.m2_space + self.m2_width,
self.m1_space + 0.5 *contact.poly.width + 0.5 * self.m1_width)
active_spacing = max(self.m1_space, 0.5*contact.poly.first_layer_width + drc("poly_to_active"))
active_spacing = max(self.m1_space,
0.5 * contact.poly.first_layer_width + drc("poly_to_active"))
inputC_yoffset = self.nmos3_pos.y + self.nmos.active_height + active_spacing
self.route_input_gate(self.pmos3_inst, self.nmos3_inst, inputC_yoffset, "C", position="center")
self.route_input_gate(self.pmos3_inst,
self.nmos3_inst,
inputC_yoffset,
"C",
position="center")
inputB_yoffset = inputC_yoffset + metal_spacing
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
self.inputA_yoffset = inputB_yoffset + metal_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A", position="center")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A",
position="center")
def route_output(self):
""" Route the Z output """
# PMOS1 drain
# PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain
# PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D")
# 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
self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -229,10 +245,10 @@ class pnand3(pgate.pgate):
offset=nmos3_pin.center())
# PMOS3 and NMOS3 are drain aligned
self.add_path("metal2",[pmos3_pin.bc(), nmos3_pin.uc()])
self.add_path("metal2", [pmos3_pin.bc(), nmos3_pin.uc()])
# Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset)
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -256,21 +272,32 @@ class pnand3(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1094
return transition_prob*(c_load + c_para)
return transition_prob *(c_load + c_para)
def input_load(self):
"""Return the relative input capacitance of a single input"""
return self.nmos_size+self.pmos_size
return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True):
"""Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall. Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise)
Returns an object representing the parameters for delay in tau units.
Optional is_rise refers to the input direction rise/fall.
Input inverted by this stage.
"""
parasitic_delay = 3
return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise)
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -10,10 +10,9 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory
class pnor2(pgate.pgate):
"""
This module generates gds of a parametrically sized 2-input nor.
@ -22,22 +21,23 @@ class pnor2(pgate.pgate):
def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """
debug.info(2, "creating pnor2 structure {0} with size of {1}".format(name, size))
debug.info(2,
"creating pnor2 structure {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
self.nmos_size = size
# We will just make this 1.5 times for now. NORs are not ideal anyhow.
self.pmos_size = 1.5*parameter["beta"]*size
self.nmos_width = self.nmos_size*drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx")
self.pmos_size = 1.5 * parameter["beta"] * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized
debug.check(size==1,"Size 1 pnor2 is only supported now.")
debug.check(size==1, "Size 1 pnor2 is only supported now.")
self.tx_mults = 1
# Creates the netlist and layout
pgate.pgate.__init__(self, name, height)
def create_netlist(self):
self.add_pins()
@ -89,44 +89,48 @@ class pnor2(pgate.pgate):
self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width)
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# Compute the other pmos2 location, but determining
# offset to overlap the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width \
+ self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width
# Height is an input parameter, so it is not recomputed.
# This is the extra space needed to ensure DRC rules to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(),0)
# This is the extra space needed to ensure DRC rules
# to the active contacts
extra_contact_space = max(-self.nmos.get_pin("D").by(), 0)
# This is a poly-to-poly of a flipped cell
self.top_bottom_space = max(0.5*self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"), self.poly_space)
self.top_bottom_space = max(0.5 * self.m1_width + self.m1_space + extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
self.pmos1_inst=self.add_inst(name="pnor2_pmos1",
mod=self.pmos)
self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
@ -134,23 +138,23 @@ class pnor2(pgate.pgate):
self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst=self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst=self.add_inst(name="pnor2_nmos2",
mod=self.nmos)
self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos)
self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self):
"""
"""
Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos1_pos = vector(self.pmos.active_offset.x,
self.height - self.pmos.active_height - self.top_bottom_space)
self.height - self.pmos.active_height \
- self.top_bottom_space)
self.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset
@ -162,42 +166,49 @@ class pnor2(pgate.pgate):
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,0.5*(pmos1_pos.y+nmos1_pos.y+self.nmos.active_height))
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.nmos2_inst,"D","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.nmos2_inst, "D", "gnd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def route_inputs(self):
""" Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later!
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center")
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
+ self.m2_space + self.m2_width
self.route_input_gate(self.pmos2_inst,
self.nmos2_inst,
inputB_yoffset,
"B",
position="center")
# This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.inputA_yoffset, "A")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.inputA_yoffset,
"A")
def route_output(self):
""" Route the Z output """
# PMOS2 drain
# PMOS2 drain
pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain
nmos_pin = self.nmos1_inst.get_pin("D")
@ -207,17 +218,18 @@ class pnor2(pgate.pgate):
# Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center())
m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width,0)
mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset])
self.add_path("metal2",
[pmos_pin.bc(), mid2_offset, mid3_offset])
self.add_path("metal2",
[nmos_pin.rc(), mid1_offset, mid2_offset])
# This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset)
@ -240,10 +252,11 @@ class pnor2(pgate.pgate):
def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF"""
c_load = load
c_para = spice["min_tx_drain_c"]*(self.nmos_size/parameter["min_tx_size"])#ff
# In fF
c_para = spice["min_tx_drain_c"] * (self.nmos_size / parameter["min_tx_size"])
transition_prob = 0.1875
return transition_prob*(c_load + c_para)
return transition_prob * (c_load + c_para)
def build_graph(self, graph, inst_name, port_nets):
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
self.add_graph_edges(graph, port_nets)

View File

@ -13,6 +13,7 @@ from vector import vector
from globals import OPTS
from sram_factory import factory
class precharge(design.design):
"""
Creates a single precharge cell
@ -25,16 +26,15 @@ class precharge(design.design):
self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"]
self.ptx_width = self.beta*parameter["min_tx_size"]
self.ptx_width = self.beta * parameter["min_tx_size"]
self.width = self.bitcell.width
self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br
# Creates the netlist and layout
# Since it has variable height, it is not a pgate.
self.create_netlist()
if not OPTS.netlist_only:
if not OPTS.netlist_only:
self.create_layout()
self.DRC_LVS()
@ -53,7 +53,8 @@ class precharge(design.design):
self.connect_to_bitlines()
def add_pins(self):
self.add_pin_list(["bl", "br", "en_bar", "vdd"], ["OUTPUT", "OUTPUT", "INPUT", "POWER"])
self.add_pin_list(["bl", "br", "en_bar", "vdd"],
["OUTPUT", "OUTPUT", "INPUT", "POWER"])
def add_ptx(self):
"""
@ -64,16 +65,13 @@ class precharge(design.design):
tx_type="pmos")
self.add_mod(self.pmos)
def route_vdd_rail(self):
"""
Adds a vdd rail at the top of the cell
"""
# Adds the rail across the width of the cell
vdd_position = vector(0.5*self.width, self.height)
vdd_position = vector(0.5 * self.width, self.height)
self.add_rect_center(layer="metal1",
offset=vdd_position,
width=self.width,
@ -87,44 +85,43 @@ class precharge(design.design):
# Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self):
"""
Create both the upper_pmos and lower_pmos to the module
"""
self.lower_pmos_inst=self.add_inst(name="lower_pmos",
mod=self.pmos)
self.lower_pmos_inst = self.add_inst(name="lower_pmos",
mod=self.pmos)
self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1",
mod=self.pmos)
self.upper_pmos1_inst = self.add_inst(name="upper_pmos1",
mod=self.pmos)
self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2",
mod=self.pmos)
self.upper_pmos2_inst = self.add_inst(name="upper_pmos2",
mod=self.pmos)
self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self):
"""
Place both the upper_pmos and lower_pmos to the module
"""
# Compute the other pmos2 location, but determining offset to overlap the
# source and drain pins
# Compute the other pmos2 location,
# but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll()
# This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx()
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff, self.well_enclose_active),
self.lower_pmos_position = vector(max(bl_xoffset - contact_xdiff,
self.well_enclose_active),
self.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout
ydiff = self.pmos.height + 2*self.m1_space + contact.poly.width
ydiff = self.pmos.height + 2 * self.m1_space + contact.poly.width
self.upper_pmos1_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos)
@ -146,7 +143,9 @@ class precharge(design.design):
# connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width)
xlength = self.upper_pmos2_inst.get_pin("G").lx() - self.upper_pmos1_inst.get_pin("G").lx() + self.poly_width
xlength = self.upper_pmos2_inst.get_pin("G").lx() \
- self.upper_pmos1_inst.get_pin("G").lx() \
+ self.poly_width
self.add_rect(layer="poly",
offset=offset,
width=xlength,
@ -158,16 +157,16 @@ class precharge(design.design):
"""
# adds the en contact to connect the gates to the en rail on metal1
offset = self.lower_pmos_inst.get_pin("G").ul() + vector(0,0.5*self.poly_space)
offset = self.lower_pmos_inst.get_pin("G").ul() \
+ vector(0, 0.5 * self.poly_space)
self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset)
# adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar",
layer="metal1",
start=offset.scale(0,1),
end=offset.scale(0,1)+vector(self.width,0))
start=offset.scale(0, 1),
end=offset.scale(0, 1) + vector(self.width, 0))
def place_nwell_and_contact(self):
"""
@ -175,8 +174,9 @@ class precharge(design.design):
"""
# adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height/2 + drc("well_extend_active"))
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1, 0) \
+ vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \
+ drc("well_extend_active"))
self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos,
implant_type="n",
@ -187,18 +187,18 @@ class precharge(design.design):
# nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell",
offset=vector(0,0),
offset=vector(0, 0),
width=self.width,
height=self.height)
def route_bitlines(self):
"""
Adds both bit-line and bit-line-bar to the module
"""
# adds the BL on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_bl).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2",
offset=offset,
@ -206,7 +206,8 @@ class precharge(design.design):
height=self.height)
# adds the BR on metal 2
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(),0) - vector(0.5 * self.m2_width,0)
offset = vector(self.bitcell.get_pin(self.bitcell_br).cx(), 0) \
- vector(0.5 * self.m2_width, 0)
self.br_pin = self.add_layout_pin(text="br",
layer="metal2",
offset=offset,
@ -218,60 +219,64 @@ class precharge(design.design):
Connect the bitlines to the devices
"""
self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br"))
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),
self.get_pin("br"))
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),
self.get_pin("br"))
def add_bitline_contacts(self):
"""
Adds contacts/via from metal1 to metal2 for bit-lines
"""
stack=("metal1", "via1", "metal2")
stack = ("metal1", "via1", "metal2")
upper_pin = self.upper_pmos1_inst.get_pin("S")
lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor
self.bl_contact=self.add_via_center(layers=stack,
offset=upper_pin.center(),
directions=("V","V"))
self.bl_contact =self.add_via_center(layers=stack,
offset=upper_pin.center(),
directions=("V", "V"))
self.add_via_center(layers=stack,
offset=lower_pin.center(),
directions=("V","V"))
directions=("V", "V"))
# BR routes over on M1 first
self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V","V"))
offset=vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V", "V"))
self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V","V"))
offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V", "V"))
def connect_pmos_m1(self, pmos_pin, bit_pin):
"""
Connect a pmos pin to bitline pin
"""
Connect a pmos pin to bitline pin
"""
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal1", [ left_pos, right_pos] )
self.add_path("metal1", [left_pos, right_pos] )
def connect_pmos_m2(self, pmos_pin, bit_pin):
"""
Connect a pmos pin to bitline pin
"""
Connect a pmos pin to bitline pin
"""
left_pos = vector(min(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(),bit_pin.cx()), pmos_pin.cy())
left_pos = vector(min(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
right_pos = vector(max(pmos_pin.cx(), bit_pin.cx()), pmos_pin.cy())
self.add_path("metal2", [ left_pos, right_pos], self.bl_contact.height)
self.add_path("metal2", [left_pos, right_pos], self.bl_contact.height)
def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell"""
#The enable connect to three pmos gates. They all use the same size pmos.
"""Get the relative capacitance of the enable in the precharge cell"""
# The enable connect to three pmos gates
# They all use the same size pmos.
pmos_cin = self.pmos.get_cin()
return 3*pmos_cin
return 3 * pmos_cin

View File

@ -10,16 +10,12 @@ import pgate
import debug
from tech import drc, parameter, spice
from vector import vector
from math import ceil
from globals import OPTS
from utils import round_to_grid
import logical_effort
from sram_factory import factory
class ptristate_inv(pgate.pgate):
"""
ptristate generates gds of a parametrically sized tristate inverter.
ptristate generates gds of a parametrically sized tristate inverter.
There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height.
@ -27,14 +23,16 @@ class ptristate_inv(pgate.pgate):
def __init__(self, name, size=1, height=None):
debug.info(2, "creating ptristate inv {0} with size of {1}".format(name, size))
debug.info(2,
"creating ptristate inv {0} with size of {1}".format(name,
size))
self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices
self.size = 2*size
self.size = 2 * size
self.nmos_size = size
self.beta = parameter["beta"]
self.pmos_size = self.beta*size
self.pmos_size = self.beta * size
self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size * drc("minwidth_tx")
@ -75,10 +73,10 @@ class ptristate_inv(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + drc("well_enclosure_active")
self.well_width = 2 * self.pmos.active_width + drc("well_enclosure_active")
# Add an extra space because we route the output on the right of the S/D
self.width = self.well_width + 0.5*self.m1_space
self.width = self.well_width + 0.5 * self.m1_space
# Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below
@ -104,43 +102,44 @@ class ptristate_inv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd",
layer="metal1",
offset=vector(0.5*self.width,0),
offset=vector(0.5 * self.width, 0),
width=self.width)
self.add_layout_pin_rect_center(text="vdd",
layer="metal1",
offset=vector(0.5*self.width,self.height),
offset=vector(0.5 * self.width, self.height),
width=self.width)
def create_ptx(self):
"""
"""
Create the PMOS and NMOS netlist.
"""
# These are the inverter PMOS/NMOS
self.pmos1_inst=self.add_inst(name="ptri_pmos1", mod=self.pmos)
self.pmos1_inst = self.add_inst(name="ptri_pmos1",
mod=self.pmos)
self.connect_inst(["vdd", "in", "n1", "vdd"])
self.nmos1_inst=self.add_inst(name="ptri_nmos1", mod=self.nmos)
self.nmos1_inst = self.add_inst(name="ptri_nmos1",
mod=self.nmos)
self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"])
self.nmos2_inst=self.add_inst(name="ptri_nmos2", mod=self.nmos)
self.nmos2_inst = self.add_inst(name="ptri_nmos2",
mod=self.nmos)
self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self):
"""
"""
Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel
"""
pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height
nmos_yoff = self.top_bottom_space + 0.5*contact.well.height
pmos_yoff = self.height - self.pmos.active_height \
- self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
# Tristate transistors
pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff)
@ -154,21 +153,24 @@ class ptristate_inv(pgate.pgate):
self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height))
# Output position will be in between the PMOS and NMOS
self.output_pos = vector(0,
0.5 * (pmos_yoff + nmos_yoff + self.nmos.height))
# This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy())
# This will help with the wells
self.well_pos = vector(0, self.nmos1_inst.uy())
def route_inputs(self):
""" Route the gates """
self.route_input_gate(self.pmos1_inst, self.nmos1_inst, self.output_pos.y, "in", position="farleft")
self.route_input_gate(self.pmos1_inst,
self.nmos1_inst,
self.output_pos.y,
"in",
position="farleft")
self.route_single_gate(self.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self):
""" Route the output (drains) together. """
@ -181,40 +183,41 @@ class ptristate_inv(pgate.pgate):
self.add_layout_pin(text="out",
layer="metal1",
offset=nmos_drain_pos,
height=pmos_drain_pos.y-nmos_drain_pos.y)
height=pmos_drain_pos.y - nmos_drain_pos.y)
def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies AFTER the wells are created """
"""
Add n/p well taps to the layout and connect to
supplies AFTER the wells are created
"""
layer_stack = ("active", "contact", "metal1")
drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center()
self.nwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,vdd_pos.y),
implant_type="n",
well_type="n")
self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x, vdd_pos.y),
implant_type="n",
well_type="n")
gnd_pos = self.get_pin("gnd").center()
self.pwell_contact=self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,gnd_pos.y),
implant_type="p",
well_type="p")
self.pwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x, gnd_pos.y),
implant_type="p",
well_type="p")
def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """
self.connect_pin_to_rail(self.nmos1_inst,"S","gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.nmos1_inst, "S", "gnd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
# Power in this module currently not defined.
# Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power
def get_cin(self):
return 9*spice["min_tx_gate_c"]
return 9 * spice["min_tx_gate_c"]

View File

@ -9,9 +9,9 @@ import design
import debug
from tech import drc, spice
from vector import vector
from globals import OPTS
from sram_factory import factory
class ptx(design.design):
"""
This module generates gds and spice of a parametrically NMOS or
@ -21,7 +21,14 @@ class ptx(design.design):
you to connect the fingered gates and active for parallel devices.
"""
def __init__(self, name="", width=drc("minwidth_tx"), mults=1, tx_type="nmos", connect_active=False, connect_poly=False, num_contacts=None):
def __init__(self,
name="",
width=drc("minwidth_tx"),
mults=1,
tx_type="nmos",
connect_active=False,
connect_poly=False,
num_contacts=None):
# We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't
@ -34,7 +41,7 @@ class ptx(design.design):
if num_contacts:
name += "_c{}".format(num_contacts)
# replace periods with underscore for newer spice compatibility
name=name.replace('.','_')
name = name.replace('.', '_')
debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name)
@ -51,42 +58,43 @@ class ptx(design.design):
# We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell
self.create_layout()
def create_layout(self):
"""Calls all functions related to the generation of the layout"""
self.setup_layout_constants()
self.add_active()
self.add_well_implant()
self.add_well_implant()
self.add_poly()
self.add_active_contacts()
self.translate_all(self.active_offset)
# for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes
#self.DRC()
# self.DRC()
def create_netlist(self):
pin_list = ["D", "G", "S", "B"]
if self.tx_type=="nmos":
if self.tx_type == "nmos":
body_dir = 'GROUND'
else: #Assumed that the check for either pmos or nmos is done elsewhere.
else:
# Assumed that the check for either pmos or nmos is done elsewhere.
body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins)))
# Just make a guess since these will actually be decided in the layout later.
area_sd = 2.5*drc("minwidth_poly")*self.tx_width
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width
self.spice_device="M{{0}} {{1}} {0} m={1} w={2}u l={3}u pd={4:.2f}u ps={4:.2f}u as={5:.2f}p ad={5:.2f}p".format(spice[self.tx_type],
self.mults,
self.tx_width,
drc("minwidth_poly"),
perimeter_sd,
area_sd)
# Just make a guess since these will actually
# be decided in the layout later.
area_sd = 2.5 * drc("minwidth_poly") * self.tx_width
perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
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.append("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name))
@ -116,38 +124,39 @@ class ptx(design.design):
# The contacted poly pitch (or uncontacted in an odd technology)
self.poly_pitch = max(2*self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_pitch = max(2 * self.contact_to_gate + self.contact_width + self.poly_width,
self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology)
self.contact_pitch = 2*self.contact_to_gate + self.contact_width + self.poly_width
self.contact_pitch = 2 * self.contact_to_gate + self.contact_width + self.poly_width
# The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(drc("active_enclosure_contact"),
(self.active_width - self.contact_width)/2)
(self.active_width - self.contact_width) / 2)
# This is the distance from the edge of poly to the contacted end of active
self.end_to_poly = active_enclose_contact + self.contact_width + self.contact_to_gate
# Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches
self.active_width = 2*self.end_to_poly + self.poly_width + (self.mults - 1)*self.poly_pitch
self.active_width = 2 * self.end_to_poly + self.poly_width + (self.mults - 1) * self.poly_pitch
# Active height is just the transistor width
self.active_height = self.tx_width
# Poly height must include poly extension over active
self.poly_height = self.tx_width + 2*self.poly_extend_active
self.poly_height = self.tx_width + 2 * self.poly_extend_active
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active]*2)
self.active_offset = vector([self.well_enclose_active] * 2)
# Well enclosure of active, ensure minwidth as well
if drc("has_{}well".format(self.well_type)):
self.cell_well_width = max(self.active_width + 2*self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active,
self.well_width)
self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width)
self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width)
# We are going to shift the 0,0, so include that in the width and height
self.height = self.cell_well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x
@ -157,17 +166,20 @@ class ptx(design.design):
self.width = self.active_width
# The active offset is due to the well extension
self.active_offset = vector([self.well_enclose_active]*2)
self.active_offset = vector([self.well_enclose_active] * 2)
# This is the center of the first active contact offset (centered vertically)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5*self.contact_width,
0.5*self.active_height)
self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width,
0.5 * self.active_height)
# Min area results are just flagged for now.
debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue.
debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly area violated.")
debug.check(self.active_width * self.active_height >= drc("minarea_active"),
"Minimum active area violated.")
# We do not want to increase the poly dimensions to fix
# an area problem as it would cause an LVS issue.
debug.check(self.poly_width * self.poly_height >= drc("minarea_poly"),
"Minimum poly area violated.")
def connect_fingered_poly(self, poly_positions):
"""
@ -182,13 +194,19 @@ class ptx(design.design):
# The width of the poly is from the left-most to right-most poly gate
poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
if self.tx_type == "pmos":
# This can be limited by poly to active spacing or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active)
# This can be limited by poly to active spacing
# or the poly extension
distance_below_active = self.poly_width + max(self.poly_to_active,
0.5 * self.poly_height)
poly_offset = poly_positions[0] - vector(0.5 * self.poly_width,
distance_below_active)
else:
# This can be limited by poly to active spacing or the poly extension
distance_above_active = max(self.poly_to_active,0.5*self.poly_height)
poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active)
# This can be limited by poly to active spacing
# or the poly extension
distance_above_active = max(self.poly_to_active,
0.5 * self.poly_height)
poly_offset = poly_positions[0] + vector(-0.5 * self.poly_width,
distance_above_active)
# Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin
self.add_layout_pin(text="G",
@ -205,12 +223,13 @@ class ptx(design.design):
# This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \
+ self.m1_space + 0.5*self.m1_width)
pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \
+ self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0)
# drains always go to the MIDDLE of the cell, so top of NMOS, bottom of PMOS
# drains always go to the MIDDLE of the cell,
# so top of NMOS, bottom of PMOS
# so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos":
drain_dir = -1
@ -219,17 +238,19 @@ class ptx(design.design):
drain_dir = 1
source_dir = -1
if len(source_positions)>1:
if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment
for a in source_positions:
self.add_path(("metal1"), [a,a+pin_offset.scale(source_dir,source_dir)])
self.add_path(("metal1"),
[a, a + pin_offset.scale(source_dir,
source_dir)])
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="S",
layer="metal1",
start=source_positions[0]+source_offset-end_offset,
end=source_positions[-1]+source_offset+end_offset)
start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1] + source_offset + end_offset)
if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir)
@ -240,24 +261,27 @@ class ptx(design.design):
# Add a single horizontal pin
self.add_layout_pin_segment_center(text="D",
layer="metal1",
start=drain_positions[0]+drain_offset-end_offset,
end=drain_positions[-1]+drain_offset+end_offset)
start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self):
"""
Add the poly gates(s) and (optionally) connect them.
"""
# poly is one contacted spacing from the end and down an extension
poly_offset = self.active_offset + vector(self.poly_width,self.poly_height).scale(0.5,0.5) \
poly_offset = self.active_offset \
+ vector(self.poly_width, self.poly_height).scale(0.5, 0.5) \
+ vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates
poly_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors
for i in range(0, self.mults):
# Add this duplicate rectangle in case we remove the pin when joining fingers
# Add this duplicate rectangle in case we remove
# the pin when joining fingers
self.add_rect_center(layer="poly",
offset=poly_offset,
height=self.poly_height,
@ -274,8 +298,8 @@ class ptx(design.design):
self.connect_fingered_poly(poly_positions)
def add_active(self):
"""
Adding the diffusion (active region = diffusion region)
"""
Adding the diffusion (active region = diffusion region)
"""
self.add_rect(layer="active",
offset=self.active_offset,
@ -284,11 +308,11 @@ class ptx(design.design):
# If the implant must enclose the active, shift offset
# and increase width/height
enclose_width = drc("implant_enclosure_active")
enclose_offset = [enclose_width]*2
enclose_offset = [enclose_width] * 2
self.add_rect(layer="{}implant".format(self.implant_type),
offset=self.active_offset - enclose_offset,
width=self.active_width + 2*enclose_width,
height=self.active_height + 2*enclose_width)
width=self.active_width + 2 * enclose_width,
height=self.active_height + 2 * enclose_width)
def add_well_implant(self):
"""
@ -320,15 +344,16 @@ class ptx(design.design):
# The first one will always be a source
source_positions = [self.contact_offset]
drain_positions = []
# It is important that these are from left to right, so that the pins are in the right
# It is important that these are from left to right,
# so that the pins are in the right
# order for the accessors.
for i in range(self.mults):
if i%2:
# It's a source... so offset from previous drain.
source_positions.append(drain_positions[-1] + vector(self.contact_pitch,0))
source_positions.append(drain_positions[-1] + vector(self.contact_pitch, 0))
else:
# It's a drain... so offset from previous source.
drain_positions.append(source_positions[-1] + vector(self.contact_pitch,0))
drain_positions.append(source_positions[-1] + vector(self.contact_pitch, 0))
return [source_positions,drain_positions]
@ -371,9 +396,12 @@ class ptx(design.design):
def get_cin(self):
"""Returns the relative gate cin of the tx"""
return self.tx_width/drc("minwidth_tx")
return self.tx_width / drc("minwidth_tx")
def build_graph(self, graph, inst_name, port_nets):
"""Adds edges based on inputs/outputs. Overrides base class function."""
self.add_graph_edges(graph, port_nets)
def build_graph(self, graph, inst_name, port_nets):
"""
Adds edges based on inputs/outputs.
Overrides base class function.
"""
self.add_graph_edges(graph, port_nets)

View File

@ -9,17 +9,17 @@ import pgate
import debug
from tech import drc
from vector import vector
import contact
from globals import OPTS
from sram_factory import factory
import logical_effort
class single_level_column_mux(pgate.pgate):
"""
This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book:
Column-mux transistors driven by the decoder must be sized for optimal speed
Column-mux transistors driven by the decoder must be sized
for optimal speed
"""
def __init__(self, name, tx_size=8, bitcell_bl="bl", bitcell_br="br"):
@ -38,7 +38,7 @@ class single_level_column_mux(pgate.pgate):
def create_layout(self):
self.pin_height = 2*self.m2_width
self.pin_height = 2 * self.m2_width
self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height
self.connect_poly()
@ -50,11 +50,9 @@ class single_level_column_mux(pgate.pgate):
self.bitcell = factory.create(module_type="bitcell")
# Adds nmos_lower,nmos_upper to the module
self.ptx_width = self.tx_size*drc("minwidth_tx")
self.ptx_width = self.tx_size * drc("minwidth_tx")
self.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.add_mod(self.nmos)
def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"])
@ -68,11 +66,11 @@ class single_level_column_mux(pgate.pgate):
# bl and br
self.add_layout_pin(text="bl",
layer="metal2",
offset=bl_pos + vector(0,self.height - self.pin_height),
offset=bl_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
self.add_layout_pin(text="br",
layer="metal2",
offset=br_pos + vector(0,self.height - self.pin_height),
offset=br_pos + vector(0, self.height - self.pin_height),
height=self.pin_height)
# bl_out and br_out
@ -85,35 +83,34 @@ class single_level_column_mux(pgate.pgate):
offset=br_pos,
height=self.pin_height)
def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center
nmos_lower_position = self.nmos.active_offset.scale(0,1) + vector(0.5*self.bitcell.width-0.5*self.nmos.active_width,0)
self.nmos_lower=self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=nmos_lower_position)
nmos_lower_position = self.nmos.active_offset.scale(0,1) \
+ vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
self.nmos_lower = self.add_inst(name="mux_tx1",
mod=self.nmos,
offset=nmos_lower_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"])
# This aligns it directly above the other tx with gates abutting
nmos_upper_position = nmos_lower_position + vector(0,self.nmos.active_height + self.poly_space)
self.nmos_upper=self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos_upper_position)
nmos_upper_position = nmos_lower_position \
+ vector(0, self.nmos.active_height + self.poly_space)
self.nmos_upper = self.add_inst(name="mux_tx2",
mod=self.nmos,
offset=nmos_upper_position)
self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self):
""" Connect the poly gate of the two pass transistors """
height=self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
height = self.nmos_upper.get_pin("G").uy() - self.nmos_lower.get_pin("G").by()
self.add_layout_pin(text="sel",
layer="poly",
offset=self.nmos_lower.get_pin("G").ll(),
height=height)
def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """
# These are on metal2
@ -129,52 +126,61 @@ class single_level_column_mux(pgate.pgate):
nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/D
self.add_via_center(layers=("metal1","via1","metal2"),
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=bl_pin.bc(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=br_out_pin.uc(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_upper_s_pin.center(),
directions=("V","V"))
self.add_via_center(layers=("metal1","via1","metal2"),
directions=("V", "V"))
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_lower_d_pin.center(),
directions=("V","V"))
directions=("V", "V"))
# bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2
self.add_path("metal1",[bl_pin.ll(), vector(nmos_upper_d_pin.cx(),bl_pin.by()), nmos_upper_d_pin.center()])
self.add_path("metal1",
[bl_pin.ll(), vector(nmos_upper_d_pin.cx(), bl_pin.by()),
nmos_upper_d_pin.center()])
# halfway up, move over
mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4)
mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4)
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
mid1 = bl_out_pin.uc().scale(1, 0.4) \
+ nmos_upper_s_pin.bc().scale(0, 0.4)
mid2 = bl_out_pin.uc().scale(0, 0.4) \
+ nmos_upper_s_pin.bc().scale(1, 0.4)
self.add_path("metal2",
[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()])
# br -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1
self.add_path("metal1",[br_out_pin.uc(), vector(nmos_lower_s_pin.cx(),br_out_pin.uy()), nmos_lower_s_pin.center()])
self.add_path("metal1",
[br_out_pin.uc(),
vector(nmos_lower_s_pin.cx(), br_out_pin.uy()),
nmos_lower_s_pin.center()])
# halfway up, move over
mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
mid1 = br_pin.bc().scale(1,0.5) \
+ nmos_lower_d_pin.uc().scale(0,0.5)
mid2 = br_pin.bc().scale(0,0.5) \
+ nmos_lower_d_pin.uc().scale(1,0.5)
self.add_path("metal2",
[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()])
def add_wells(self):
"""
"""
Add a well and implant over the whole cell. Also, add the
pwell contact (if it exists)
pwell contact (if it exists)
"""
# Add it to the right, aligned in between the two tx
active_pos = vector(self.bitcell.width,self.nmos_upper.by() - 0.5*self.poly_space)
active_via = self.add_via_center(layers=("active", "contact", "metal1"),
offset=active_pos,
implant_type="p",
well_type="p")
active_pos = vector(self.bitcell.width,
self.nmos_upper.by() - 0.5 * self.poly_space)
self.add_via_center(layers=("active", "contact", "metal1"),
offset=active_pos,
implant_type="p",
well_type="p")
# Add the M1->M2->M3 stack
# Add the M1->M2->M3 stack
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=active_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"),
@ -185,13 +191,22 @@ class single_level_column_mux(pgate.pgate):
# Add well enclosure over all the tx and contact
self.add_rect(layer="pwell",
offset=vector(0,0),
offset=vector(0, 0),
width=self.bitcell.width,
height=self.height)
def get_stage_effort(self, corner, slew, load):
"""Returns relative delay that the column mux. Difficult to convert to LE model."""
"""
Returns relative delay that the column mux.
Difficult to convert to LE model.
"""
parasitic_delay = 1
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect.
return logical_effort.logical_effort('column_mux', self.tx_size, cin, load, parasitic_delay, False)
# This is not CMOS, so using this may be incorrect.
cin = 2 * self.tx_size
return logical_effort.logical_effort("column_mux",
self.tx_size,
cin,
load,
parasitic_delay,
False)

View File

@ -5,9 +5,9 @@
# (acting for and on behalf of Oklahoma State University)
# All rights reserved.
#
import debug
from globals import OPTS
class sram_factory:
"""
This is a factory pattern to create modules for usage in an SRAM.
@ -19,7 +19,7 @@ class sram_factory:
"""
def __init__(self):
# A dictionary of modules indexed by module type
# A dictionary of modules indexed by module type
self.modules = {}
# These are the indices to append to name to make unique object names
self.module_indices = {}
@ -34,8 +34,8 @@ class sram_factory:
def create(self, module_type, **kwargs):
"""
A generic function to create a module with a given module_type. The args
are passed directly to the module constructor.
A generic function to create a module with a given module_type.
The args are passed directly to the module constructor.
"""
# if name!="":
# # This is a special case where the name and type don't match
@ -58,28 +58,30 @@ class sram_factory:
self.objects[module_type] = []
# Either retreive a previous object or create a new one
#print("new",kwargs)
for obj in self.objects[module_type]:
(obj_kwargs, obj_item) = obj
# Must have the same dictionary exactly (conservative)
if obj_kwargs == kwargs:
#debug.info(0, "Existing module: type={0} name={1} kwargs={2}".format(module_type, obj_item.name, str(kwargs)))
return obj_item
#else:
# print("obj",obj_kwargs)
# Use the default name if there are default arguments
# This is especially for library cells so that the spice and gds files can be found.
if len(kwargs)>0:
# This is especially for library cells so that the
# spice and gds files can be found.
if len(kwargs) > 0:
# Create a unique name and increment the index
module_name = "{0}_{1}".format(module_type, self.module_indices[module_type])
module_name = "{0}_{1}".format(module_type,
self.module_indices[module_type])
self.module_indices[module_type] += 1
else:
module_name = module_type
#debug.info(0, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs)))
obj = mod(name=module_name,**kwargs)
self.objects[module_type].append((kwargs,obj))
# type_str = "type={}".format(module_type)
# name_str = "name={}".format(module_name)
# kwargs_str = "kwargs={}".format(str(kwargs))
# import debug
# debug.info(0, "New module:" + type_str + name_str + kwargs_str)
obj = mod(name=module_name, **kwargs)
self.objects[module_type].append((kwargs, obj))
return obj
def get_mods(self, module_type):
@ -90,11 +92,11 @@ class sram_factory:
module_type = getattr(OPTS, module_type)
try:
mod_tuples = self.objects[module_type]
mods = [mod for kwargs,mod in mod_tuples]
mods = [mod for kwargs, mod in mod_tuples]
except KeyError:
mods = []
return mods
# Make a factory
factory = sram_factory()

View File

@ -61,27 +61,27 @@ class timing_sram_test(openram_test):
data.update(port_data[0])
if OPTS.tech_name == "freepdk45":
golden_data = {'delay_hl': [0.2181231],
'delay_lh': [0.2181231],
'leakage_power': 0.0025453999999999997,
'min_period': 0.781,
'read0_power': [0.34664159999999994],
'read1_power': [0.32656349999999995],
'slew_hl': [0.21136519999999998],
'slew_lh': [0.21136519999999998],
'write0_power': [0.37980179999999997],
'write1_power': [0.3532026]}
golden_data = {'delay_hl': [0.2383338],
'delay_lh': [0.2383338],
'leakage_power': 0.0014532999999999998,
'min_period': 0.898,
'read0_power': [0.30059800000000003],
'read1_power': [0.30061810000000005],
'slew_hl': [0.25358420000000004],
'slew_lh': [0.25358420000000004],
'write0_power': [0.34616749999999996],
'write1_power': [0.2792924]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.4082],
'delay_lh': [1.4082],
'leakage_power': 0.0267388,
'min_period': 4.688,
'read0_power': [11.5255],
'read1_power': [10.9406],
'slew_hl': [1.2979],
'slew_lh': [1.2979],
'write0_power': [12.9458],
'write1_power': [11.7444]}
golden_data = {'delay_hl': [1.7448],
'delay_lh': [1.7448],
'leakage_power': 0.0006356744000000001,
'min_period': 6.25,
'read0_power': [12.9846],
'read1_power': [12.9722],
'slew_hl': [1.7433],
'slew_lh': [1.7433],
'write0_power': [14.8772],
'write1_power': [11.7217]}
else:
self.assertTrue(False) # other techs fail
# Check if no too many or too few results

View File

@ -42,10 +42,10 @@ class timing_setup_test(openram_test):
'setup_times_HL': [0.026855499999999997],
'setup_times_LH': [0.032959]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'hold_times_HL': [-0.0891113],
'hold_times_LH': [-0.0769043],
'setup_times_HL': [0.1184082],
'setup_times_LH': [0.1733398]}
golden_data = {'hold_times_HL': [-0.0805664],
'hold_times_LH': [-0.11718749999999999],
'setup_times_HL': [0.16357419999999998],
'setup_times_LH': [0.1757812]}
else:
self.assertTrue(False) # other techs fail

View File

@ -65,16 +65,16 @@ class timing_sram_test(openram_test):
'write0_power': [0.36360849999999995],
'write1_power': [0.3486931]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.7083549999999998],
'delay_lh': [1.7083549999999998],
'leakage_power': 0.001119657,
'min_period': 7.812,
'read0_power': [8.013845],
'read1_power': [7.6889389999999995],
'slew_hl': [1.31918],
'slew_lh': [1.31918],
'write0_power': [8.791557000000001],
'write1_power': [8.70443]}
golden_data = {'delay_hl': [1.85985],
'delay_lh': [1.85985],
'leakage_power': 0.008613619,
'min_period': 6.875,
'read0_power': [12.656310000000001],
'read1_power': [12.11682],
'slew_hl': [1.868942],
'slew_lh': [1.868942],
'write0_power': [13.978110000000001],
'write1_power': [11.437930000000001]}
else:
self.assertTrue(False) # other techs fail

View File

@ -42,10 +42,10 @@ class timing_setup_test(openram_test):
'setup_times_HL': [0.02685547],
'setup_times_LH': [0.03295898]}
elif OPTS.tech_name == "scn4m_subm":
golden_data = {'hold_times_HL': [-0.08911132999999999],
'hold_times_LH': [-0.0769043],
'setup_times_HL': [0.1184082],
'setup_times_LH': [0.1672363]}
golden_data = {'hold_times_HL': [-0.08056640999999999],
'hold_times_LH': [-0.1293945],
'setup_times_HL': [0.1757812],
'setup_times_LH': [0.1879883]}
else:
self.assertTrue(False) # other techs fail

View File

@ -9,8 +9,8 @@
import pstats
p = pstats.Stats("profile.dat")
p.strip_dirs()
#p.sort_stats("cumulative")
# p.sort_stats("cumulative")
p.sort_stats("tottime")
#p.print_stats(50)
# p.print_stats(50)
p.print_stats()

View File

@ -5,6 +5,36 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845
+ NSUB=6E16 U0=461 K1=0.5705 TOX=13.9n VERSION=3.3.0)
.MODEL n NMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = 0.4964448
+K1 = 0.5307769 K2 = 0.0199705 K3 = 0.2963637
+K3B = 0.2012165 W0 = 2.836319E-6 NLX = 2.894802E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 0.112017 DVT1 = 0.2453972 DVT2 = -0.171915
+U0 = 444.9381976 UA = 2.921284E-10 UB = 1.773281E-18
+UC = 7.067896E-11 VSAT = 1.130785E5 A0 = 1.1356246
+AGS = 0.2810374 B0 = 2.844393E-7 B1 = 5E-6
+KETA = -7.8181E-3 A1 = 0 A2 = 1
+RDSW = 925.2701982 PRWG = -1E-3 PRWB = -1E-3
+WR = 1 WINT = 7.186965E-8 LINT = 1.735515E-9
+XL = -2E-8 XW = 0 DWG = -1.712973E-8
+DWB = 5.851691E-9 VOFF = -0.132935 NFACTOR = 0.5710974
+CIT = 0 CDSC = 8.607229E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 2.128321E-3 ETAB = 0
+DSUB = 0.0257957 PCLM = 0.6766314 PDIBLC1 = 1
+PDIBLC2 = 1.787424E-3 PDIBLCB = 0 DROUT = 0.7873539
+PSCBE1 = 6.973485E9 PSCBE2 = 1.46235E-7 PVAG = 0.05
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 1.96E-10 CGSO = 1.96E-10
+CGBO = 0 CJ = 9.276962E-4 PB = 0.8157962
+MJ = 0.3557696 CJSW = 3.181055E-10 PBSW = 0.6869149
+MJSW = 0.1 PVTH0 = -0.0252481 PRDSW = -96.4502805
+PK2 = -4.805372E-3 WKETA = -7.643187E-4 LKETA = -0.0129496 )

View File

@ -5,5 +5,35 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0)
.MODEL p PMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = -0.6636594
+K1 = 0.4564781 K2 = -0.019447 K3 = 39.382919
+K3B = -2.8930965 W0 = 2.655585E-6 NLX = 1.51028E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 1.1744581 DVT1 = 0.7631128 DVT2 = -0.1035171
+U0 = 151.3305606 UA = 2.061211E-10 UB = 1.823477E-18
+UC = -8.97321E-12 VSAT = 9.915604E4 A0 = 1.1210053
+AGS = 0.3961954 B0 = 6.493139E-7 B1 = 4.273215E-6
+KETA = -9.27E-3 A1 = 0 A2 = 1
+RDSW = 2.30725E3 PRWG = -1E-3 PRWB = 0
+WR = 1 WINT = 5.962233E-8 LINT = 4.30928E-9
+XL = -2E-8 XW = 0 DWG = -1.596201E-8
+DWB = 1.378919E-8 VOFF = -0.15 NFACTOR = 2
+CIT = 0 CDSC = 6.593084E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 0.0286461 ETAB = 0
+DSUB = 0.2436027 PCLM = 4.3597508 PDIBLC1 = 7.447024E-4
+PDIBLC2 = 4.256073E-3 PDIBLCB = 0 DROUT = 0.0120292
+PSCBE1 = 1.347622E10 PSCBE2 = 5E-9 PVAG = 3.669793
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 2.307E-10 CGSO = 2.307E-10
+CGBO = 0 CJ = 1.420282E-3 PB = 0.99
+MJ = 0.5490877 CJSW = 4.773605E-10 PBSW = 0.99
+MJSW = 0.1997417 PVTH0 = 6.58707E-3 PRDSW = -93.5582228
+PK2 = 1.011593E-3 WKETA = -0.0101398 LKETA = 6.027967E-3 )

View File

@ -5,5 +5,36 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845
+ NSUB=6E16 U0=458 K1=0.5705 TOX=13.9n VERSION=3.3.0)
.MODEL n NMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = 0.4964448
+K1 = 0.5307769 K2 = 0.0199705 K3 = 0.2963637
+K3B = 0.2012165 W0 = 2.836319E-6 NLX = 2.894802E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 0.112017 DVT1 = 0.2453972 DVT2 = -0.171915
+U0 = 444.9381976 UA = 2.921284E-10 UB = 1.773281E-18
+UC = 7.067896E-11 VSAT = 1.130785E5 A0 = 1.1356246
+AGS = 0.2810374 B0 = 2.844393E-7 B1 = 5E-6
+KETA = -7.8181E-3 A1 = 0 A2 = 1
+RDSW = 925.2701982 PRWG = -1E-3 PRWB = -1E-3
+WR = 1 WINT = 7.186965E-8 LINT = 1.735515E-9
+XL = -2E-8 XW = 0 DWG = -1.712973E-8
+DWB = 5.851691E-9 VOFF = -0.132935 NFACTOR = 0.5710974
+CIT = 0 CDSC = 8.607229E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 2.128321E-3 ETAB = 0
+DSUB = 0.0257957 PCLM = 0.6766314 PDIBLC1 = 1
+PDIBLC2 = 1.787424E-3 PDIBLCB = 0 DROUT = 0.7873539
+PSCBE1 = 6.973485E9 PSCBE2 = 1.46235E-7 PVAG = 0.05
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 1.96E-10 CGSO = 1.96E-10
+CGBO = 0 CJ = 9.276962E-4 PB = 0.8157962
+MJ = 0.3557696 CJSW = 3.181055E-10 PBSW = 0.6869149
+MJSW = 0.1 PVTH0 = -0.0252481 PRDSW = -96.4502805
+PK2 = -4.805372E-3 WKETA = -7.643187E-4 LKETA = -0.0129496 )

View File

@ -5,5 +5,35 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0)
.MODEL p PMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = -0.6636594
+K1 = 0.4564781 K2 = -0.019447 K3 = 39.382919
+K3B = -2.8930965 W0 = 2.655585E-6 NLX = 1.51028E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 1.1744581 DVT1 = 0.7631128 DVT2 = -0.1035171
+U0 = 151.3305606 UA = 2.061211E-10 UB = 1.823477E-18
+UC = -8.97321E-12 VSAT = 9.915604E4 A0 = 1.1210053
+AGS = 0.3961954 B0 = 6.493139E-7 B1 = 4.273215E-6
+KETA = -9.27E-3 A1 = 0 A2 = 1
+RDSW = 2.30725E3 PRWG = -1E-3 PRWB = 0
+WR = 1 WINT = 5.962233E-8 LINT = 4.30928E-9
+XL = -2E-8 XW = 0 DWG = -1.596201E-8
+DWB = 1.378919E-8 VOFF = -0.15 NFACTOR = 2
+CIT = 0 CDSC = 6.593084E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 0.0286461 ETAB = 0
+DSUB = 0.2436027 PCLM = 4.3597508 PDIBLC1 = 7.447024E-4
+PDIBLC2 = 4.256073E-3 PDIBLCB = 0 DROUT = 0.0120292
+PSCBE1 = 1.347622E10 PSCBE2 = 5E-9 PVAG = 3.669793
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 2.307E-10 CGSO = 2.307E-10
+CGBO = 0 CJ = 1.420282E-3 PB = 0.99
+MJ = 0.5490877 CJSW = 4.773605E-10 PBSW = 0.99
+MJSW = 0.1997417 PVTH0 = 6.58707E-3 PRDSW = -93.5582228
+PK2 = 1.011593E-3 WKETA = -0.0101398 LKETA = 6.027967E-3 )

View File

@ -5,6 +5,36 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845
+ NSUB=6E16 U0=460 K1=0.5705 TOX=13.9n VERSION=3.3.0)
.MODEL n NMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = 0.4964448
+K1 = 0.5307769 K2 = 0.0199705 K3 = 0.2963637
+K3B = 0.2012165 W0 = 2.836319E-6 NLX = 2.894802E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 0.112017 DVT1 = 0.2453972 DVT2 = -0.171915
+U0 = 444.9381976 UA = 2.921284E-10 UB = 1.773281E-18
+UC = 7.067896E-11 VSAT = 1.130785E5 A0 = 1.1356246
+AGS = 0.2810374 B0 = 2.844393E-7 B1 = 5E-6
+KETA = -7.8181E-3 A1 = 0 A2 = 1
+RDSW = 925.2701982 PRWG = -1E-3 PRWB = -1E-3
+WR = 1 WINT = 7.186965E-8 LINT = 1.735515E-9
+XL = -2E-8 XW = 0 DWG = -1.712973E-8
+DWB = 5.851691E-9 VOFF = -0.132935 NFACTOR = 0.5710974
+CIT = 0 CDSC = 8.607229E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 2.128321E-3 ETAB = 0
+DSUB = 0.0257957 PCLM = 0.6766314 PDIBLC1 = 1
+PDIBLC2 = 1.787424E-3 PDIBLCB = 0 DROUT = 0.7873539
+PSCBE1 = 6.973485E9 PSCBE2 = 1.46235E-7 PVAG = 0.05
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 1.96E-10 CGSO = 1.96E-10
+CGBO = 0 CJ = 9.276962E-4 PB = 0.8157962
+MJ = 0.3557696 CJSW = 3.181055E-10 PBSW = 0.6869149
+MJSW = 0.1 PVTH0 = -0.0252481 PRDSW = -96.4502805
+PK2 = -4.805372E-3 WKETA = -7.643187E-4 LKETA = -0.0129496 )

View File

@ -5,5 +5,35 @@
* models from MOSIS or SCN4ME
*********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0)
.MODEL p PMOS ( LEVEL = 49
+VERSION = 3.1 TNOM = 27 TOX = 7.6E-9
+XJ = 1.5E-7 NCH = 1.7E17 VTH0 = -0.6636594
+K1 = 0.4564781 K2 = -0.019447 K3 = 39.382919
+K3B = -2.8930965 W0 = 2.655585E-6 NLX = 1.51028E-7
+DVT0W = 0 DVT1W = 5.3E6 DVT2W = -0.032
+DVT0 = 1.1744581 DVT1 = 0.7631128 DVT2 = -0.1035171
+U0 = 151.3305606 UA = 2.061211E-10 UB = 1.823477E-18
+UC = -8.97321E-12 VSAT = 9.915604E4 A0 = 1.1210053
+AGS = 0.3961954 B0 = 6.493139E-7 B1 = 4.273215E-6
+KETA = -9.27E-3 A1 = 0 A2 = 1
+RDSW = 2.30725E3 PRWG = -1E-3 PRWB = 0
+WR = 1 WINT = 5.962233E-8 LINT = 4.30928E-9
+XL = -2E-8 XW = 0 DWG = -1.596201E-8
+DWB = 1.378919E-8 VOFF = -0.15 NFACTOR = 2
+CIT = 0 CDSC = 6.593084E-4 CDSCD = 0
+CDSCB = 0 ETA0 = 0.0286461 ETAB = 0
+DSUB = 0.2436027 PCLM = 4.3597508 PDIBLC1 = 7.447024E-4
+PDIBLC2 = 4.256073E-3 PDIBLCB = 0 DROUT = 0.0120292
+PSCBE1 = 1.347622E10 PSCBE2 = 5E-9 PVAG = 3.669793
+DELTA = 0.01 MOBMOD = 1 PRT = 0
+UTE = -1.5 KT1 = -0.11 KT1L = 0
+KT2 = 0.022 UA1 = 4.31E-9 UB1 = -7.61E-18
+UC1 = -5.6E-11 AT = 3.3E4 WL = 0
+WLN = 1 WW = 0 WWN = 1
+WWL = 0 LL = 0 LLN = 1
+LW = 0 LWN = 1 LWL = 0
+CAPMOD = 2 CGDO = 2.307E-10 CGSO = 2.307E-10
+CGBO = 0 CJ = 1.420282E-3 PB = 0.99
+MJ = 0.5490877 CJSW = 4.773605E-10 PBSW = 0.99
+MJSW = 0.1997417 PVTH0 = 6.58707E-3 PRDSW = -93.5582228
+PK2 = 1.011593E-3 WKETA = -0.0101398 LKETA = 6.027967E-3 )

View File

@ -291,8 +291,8 @@ spice["dff_leakage"] = 1 # Leakage power of flop in nW
spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
#Logical Effort relative values for the Handmade cells
parameter["le_tau"] = 23 #In pico-seconds.
parameter["min_inv_para_delay"] = .73 #In relative delay units
parameter["le_tau"] = 18.17 #In pico-seconds.
parameter["min_inv_para_delay"] = 2.07 #In relative delay units
parameter["cap_relative_per_ff"] = .91 #Units of Relative Capacitance/ Femto-Farad
parameter["dff_clk_cin"] = 27.5 #In relative capacitance units
parameter["6tcell_wl_cin"] = 2 #In relative capacitance units