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 [FreePDK45]: https://www.eda.ncsu.edu/wiki/FreePDK45:Contents
[SCMOS]: https://www.mosis.com/files/scmos/scmos.pdf [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 hierarchy_design
import debug import debug
import utils from tech import drc, layer
from tech import drc,layer
from vector import vector from vector import vector
class contact(hierarchy_design.hierarchy_design): class contact(hierarchy_design.hierarchy_design):
""" """
Object for a contact shape with its conductor enclosures. Object for a contact shape with its conductor enclosures. Creates
Creates a contact array minimum active or poly enclosure and metal1 enclosure. a contact array minimum active or poly enclosure and metal1
This class has enclosure on two or four sides of the contact. enclosure. This class has enclosure on two or four sides of the
The direction specifies whether the first and second layer have asymmetric extension in the H or V direction. 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=""): def __init__(self, layer_stack, dimensions=(1, 1), directions=("V", "V"),
# This will ignore the name parameter since we can guarantee a unique name here 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) hierarchy_design.hierarchy_design.__init__(self, name)
debug.info(4, "create contact object {0}".format(name)) debug.info(4, "create contact object {0}".format(name))
self.add_comment("layers: {0}".format(layer_stack)) self.add_comment("layers: {0}".format(layer_stack))
self.add_comment("dimensions: {0}".format(dimensions)) self.add_comment("dimensions: {0}".format(dimensions))
if implant_type or well_type: 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.layer_stack = layer_stack
self.dimensions = dimensions self.dimensions = dimensions
self.directions = directions self.directions = directions
self.offset = vector(0,0) self.offset = vector(0, 0)
self.implant_type = implant_type 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. # Module does not have pins, but has empty pin list.
self.pins = [] self.pins = []
self.create_layout() self.create_layout()
@ -59,7 +64,7 @@ class contact(hierarchy_design.hierarchy_design):
if self.implant_type and self.well_type: if self.implant_type and self.well_type:
self.create_implant_well_enclosures() self.create_implant_well_enclosures()
elif self.implant_type or self.well_type: 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): def setup_layers(self):
""" Locally assign the layer names. """ """ Locally assign the layer names. """
@ -67,10 +72,11 @@ class contact(hierarchy_design.hierarchy_design):
(first_layer, via_layer, second_layer) = self.layer_stack (first_layer, via_layer, second_layer) = self.layer_stack
self.first_layer_name = first_layer self.first_layer_name = first_layer
self.via_layer_name = via_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 # We will use contact for DRC, but active_contact for output
if first_layer=="active" or second_layer=="active": if first_layer == "active" or second_layer == "active":
self.via_layer_name_expanded = "active_"+via_layer self.via_layer_name_expanded = "active_" + via_layer
else: else:
self.via_layer_name_expanded = via_layer self.via_layer_name_expanded = via_layer
self.second_layer_name = second_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_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)) 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
# In some technologies, the minimum width may be larger than the overlap requirement around the via, so # than the overlap requirement around the via, so
# check this for each dimension. # check this for each dimension.
if self.directions[0] == "V": if self.directions[0] == "V":
self.first_layer_horizontal_enclosure = max(first_layer_enclosure, 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, 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": elif self.directions[0] == "H":
self.first_layer_horizontal_enclosure = max(first_layer_extend, 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, 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: else:
debug.error("Invalid first layer direction.", -1) debug.error("Invalid first layer direction.", -1)
@ -117,23 +123,23 @@ class contact(hierarchy_design.hierarchy_design):
# check this for each dimension. # check this for each dimension.
if self.directions[1] == "V": if self.directions[1] == "V":
self.second_layer_horizontal_enclosure = max(second_layer_enclosure, 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, 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": elif self.directions[1] == "H":
self.second_layer_horizontal_enclosure = max(second_layer_extend, 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, 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: else:
debug.error("Invalid second layer direction.", -1) debug.error("Invalid second layer direction.", -1)
def create_contact_array(self): def create_contact_array(self):
""" Create the contact array at the origin""" """ Create the contact array at the origin"""
# offset for the via array # offset for the via array
self.via_layer_position =vector(max(self.first_layer_horizontal_enclosure,self.second_layer_horizontal_enclosure), self.via_layer_position = vector(
max(self.first_layer_vertical_enclosure,self.second_layer_vertical_enclosure)) 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]): for i in range(self.dimensions[1]):
offset = self.via_layer_position + vector(0, self.contact_pitch * i) offset = self.via_layer_position + vector(0, self.contact_pitch * i)
@ -142,15 +148,16 @@ class contact(hierarchy_design.hierarchy_design):
offset=offset, offset=offset,
width=self.contact_width, width=self.contact_width,
height=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): def create_first_layer_enclosure(self):
# this is if the first and second layers are different # 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), self.first_layer_position = vector(
max(self.second_layer_vertical_enclosure - self.first_layer_vertical_enclosure,0)) 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_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_height = self.contact_array_height + 2 * self.first_layer_vertical_enclosure
self.add_rect(layer=self.first_layer_name, self.add_rect(layer=self.first_layer_name,
offset=self.first_layer_position, offset=self.first_layer_position,
width=self.first_layer_width, width=self.first_layer_width,
@ -158,27 +165,28 @@ class contact(hierarchy_design.hierarchy_design):
def create_second_layer_enclosure(self): def create_second_layer_enclosure(self):
# this is if the first and second layers are different # 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), self.second_layer_position = vector(
max(self.first_layer_vertical_enclosure - self.second_layer_vertical_enclosure,0)) 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_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_height = self.contact_array_height + 2 * self.second_layer_vertical_enclosure
self.add_rect(layer=self.second_layer_name, self.add_rect(layer=self.second_layer_name,
offset=self.second_layer_position, offset=self.second_layer_position,
width=self.second_layer_width, width=self.second_layer_width,
height=self.second_layer_height) height=self.second_layer_height)
def create_implant_well_enclosures(self): def create_implant_well_enclosures(self):
implant_position = self.first_layer_position - [drc("implant_enclosure_active")]*2 implant_position = self.first_layer_position - [drc("implant_enclosure_active")] * 2
implant_width = self.first_layer_width + 2*drc("implant_enclosure_active") implant_width = self.first_layer_width + 2 * drc("implant_enclosure_active")
implant_height = self.first_layer_height + 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), self.add_rect(layer="{}implant".format(self.implant_type),
offset=implant_position, offset=implant_position,
width=implant_width, width=implant_width,
height=implant_height) height=implant_height)
well_position = self.first_layer_position - [drc("well_enclosure_active")]*2 well_position = self.first_layer_position - [drc("well_enclosure_active")] * 2
well_width = self.first_layer_width + 2*drc("well_enclosure_active") well_width = self.first_layer_width + 2 * drc("well_enclosure_active")
well_height = self.first_layer_height + 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), self.add_rect(layer="{}well".format(self.well_type),
offset=well_position, offset=well_position,
width=well_width, width=well_width,
@ -188,16 +196,30 @@ class contact(hierarchy_design.hierarchy_design):
""" Get total power of a module """ """ Get total power of a module """
return self.return_power() return self.return_power()
from sram_factory import factory from sram_factory import factory
# This is not instantiated and used for calculations only. # This is not instantiated and used for calculations only.
# These are static 1x1 contacts to reuse in all the design modules. # 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")) well = factory.create(module_type="contact",
active = factory.create(module_type="contact", layer_stack=("active", "contact", "metal1"), directions=("H","V")) layer_stack=("active", "contact", "metal1"),
poly = factory.create(module_type="contact", layer_stack=("poly", "contact", "metal1"), directions=("V","H")) directions=("H", "V"))
m1m2 = factory.create(module_type="contact", layer_stack=("metal1", "via1", "metal2"), directions=("H","V")) active = factory.create(module_type="contact",
m2m3 = factory.create(module_type="contact", layer_stack=("metal2", "via2", "metal3"), directions=("V","H")) 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(): 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: else:
m3m4 = None m3m4 = None

View File

@ -6,6 +6,7 @@
# All rights reserved. # All rights reserved.
# #
class delay_data(): class delay_data():
""" """
This is the delay class to represent the delay information This is the delay class to represent the delay information
@ -20,13 +21,13 @@ class delay_data():
def __str__(self): def __str__(self):
""" override print function output """ """ 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): def __add__(self, other):
""" """
Override - function (left), for delay_data: a+b != b+a 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, return delay_data(other.delay + self.delay,
other.slew) other.slew)
@ -34,7 +35,7 @@ class delay_data():
""" """
Override - function (right), for delay_data: a+b != b+a 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, return delay_data(other.delay + self.delay,
self.slew) self.slew)

View File

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

View File

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

View File

@ -122,6 +122,9 @@ class verilog:
if self.write_size: if self.write_size:
self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port)) self.vf.write(" wmask{0}_reg = wmask{0};\n".format(port))
self.vf.write(" addr{0}_reg = addr{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: if port in self.write_ports:
self.vf.write(" din{0}_reg = din{0};\n".format(port)) self.vf.write(" din{0}_reg = din{0};\n".format(port))
if port in self.read_ports: 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(" if (!csb{0}_reg)\n".format(port))
self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port)) self.vf.write(" dout{0} <= #(DELAY) mem[addr{0}_reg];\n".format(port))
self.vf.write(" end\n") 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort import bitcell_base
class bitcell(design.design):
class bitcell(bitcell_base.bitcell_base):
""" """
A single bit cell (6T, 8T, etc.) This module implements the 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 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"] pin_names = ["bl", "br", "wl", "vdd", "gnd"]
storage_nets = ['Q', 'Qbar'] storage_nets = ['Q', 'Qbar']
type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_6t", GDS["unit"], layer["boundary"]) (width, height) = utils.get_libcell_size("cell_6t",
GDS["unit"],
layer["boundary"])
pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"]) pin_map = utils.get_libcell_pins(pin_names, "cell_6t", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "cell_6t") bitcell_base.bitcell_base.__init__(self, "cell_6t")
debug.info(2, "Create bitcell") debug.info(2, "Create bitcell")
self.width = bitcell.width self.width = bitcell.width
@ -36,15 +38,9 @@ class bitcell(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_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): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = ["wl"] row_pins = ["wl"]
return row_pins return row_pins
def get_all_bitline_names(self): def get_all_bitline_names(self):
@ -64,43 +60,22 @@ class bitcell(design.design):
def get_bl_name(self, port=0): def get_bl_name(self, port=0):
"""Get bl name""" """Get bl name"""
debug.check(port==0,"One port for bitcell only.") debug.check(port == 0, "One port for bitcell only.")
return "bl" return "bl"
def get_br_name(self, port=0): def get_br_name(self, port=0):
"""Get bl name""" """Get bl name"""
debug.check(port==0,"One port for bitcell only.") debug.check(port == 0, "One port for bitcell only.")
return "br" return "br"
def get_wl_name(self, port=0): def get_wl_name(self, port=0):
"""Get wl name""" """Get wl name"""
debug.check(port==0,"One port for bitcell only.") debug.check(port == 0, "One port for bitcell only.")
return "wl" return "wl"
def analytical_power(self, corner, load): def build_graph(self, graph, inst_name, port_nets):
"""Bitcell power in nW. Only characterizes leakage.""" """
from tech import spice Adds edges based on inputs/outputs.
leakage = spice["bitcell_leakage"] Overrides base class function.
dynamic = 0 #temporary """
total_power = self.return_power(dynamic, leakage) self.add_graph_edges(graph, port_nets)
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)

View File

@ -5,13 +5,14 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer, parameter, drc
import logical_effort 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 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 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"] 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'] 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"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1rw_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # 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") debug.info(2, "Create bitcell with 1RW and 1R Port")
self.width = bitcell_1rw_1r.width self.width = bitcell_1rw_1r.width
@ -36,15 +40,11 @@ class bitcell_1rw_1r(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_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): 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), bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col), "br0_{0}".format(col),
"bl1_{0}".format(col), "bl1_{0}".format(col),
@ -57,7 +57,7 @@ class bitcell_1rw_1r(design.design):
def get_all_wl_names(self): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"] row_pins = ["wl0", "wl1"]
return row_pins return row_pins
def get_all_bitline_names(self): def get_all_bitline_names(self):
@ -97,52 +97,27 @@ class bitcell_1rw_1r(design.design):
def get_bl_name(self, port=0): def get_bl_name(self, port=0):
"""Get bl name by port""" """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) return "bl{}".format(port)
def get_br_name(self, port=0): def get_br_name(self, port=0):
"""Get bl name by port""" """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) return "br{}".format(port)
def get_wl_name(self, port=0): def get_wl_name(self, port=0):
"""Get wl name by port""" """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) return "wl{}".format(port)
def analytical_power(self, corner, load): def build_graph(self, graph, inst_name, port_nets):
"""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):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports. # Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges # Port 0 edges
graph.add_edge(pin_dict["wl0"], pin_dict["bl0"], self) 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["br0"], self)
# Port 1 edges # Port 1 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) 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["br1"], self)

View File

@ -5,13 +5,13 @@
# (acting for and on behalf of Oklahoma State University) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort 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 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 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"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
storage_nets = ['Q', 'Q_bar'] "INPUT", "INPUT", "POWER", "GROUND"]
(width,height) = utils.get_libcell_size("cell_1w_1r", GDS["unit"], layer["boundary"]) 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"]) pin_map = utils.get_libcell_pins(pin_names, "cell_1w_1r", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # 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") debug.info(2, "Create bitcell with 1W and 1R Port")
self.width = bitcell_1w_1r.width self.width = bitcell_1w_1r.width
@ -36,15 +39,11 @@ class bitcell_1w_1r(design.design):
self.add_pin_types(self.type_list) self.add_pin_types(self.type_list)
self.nets_match = self.do_nets_exist(self.storage_nets) self.nets_match = self.do_nets_exist(self.storage_nets)
def get_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): 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), bitcell_pins = ["bl0_{0}".format(col),
"br0_{0}".format(col), "br0_{0}".format(col),
"bl1_{0}".format(col), "bl1_{0}".format(col),
@ -57,7 +56,7 @@ class bitcell_1w_1r(design.design):
def get_all_wl_names(self): def get_all_wl_names(self):
""" Creates a list of all wordline pin names """ """ Creates a list of all wordline pin names """
row_pins = ["wl0", "wl1"] row_pins = ["wl0", "wl1"]
return row_pins return row_pins
def get_all_bitline_names(self): def get_all_bitline_names(self):
@ -105,40 +104,15 @@ class bitcell_1w_1r(design.design):
def get_wl_name(self, port=0): def get_wl_name(self, port=0):
"""Get wl name by port""" """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) return "wl{}".format(port)
def analytical_power(self, corner, load): def build_graph(self, graph, inst_name, port_nets):
"""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):
"""Adds edges to graph. Multiport bitcell timing graph is too complex """Adds edges to graph. Multiport bitcell timing graph is too complex
to use the add_graph_edges function.""" to use the add_graph_edges function."""
pin_dict = {pin:port for pin,port in zip(self.pins, port_nets)} pin_dict = {pin: port for pin, port in zip(self.pins, port_nets)}
#Edges hardcoded here. Essentially wl->bl/br for both ports. # Edges hardcoded here. Essentially wl->bl/br for both ports.
# Port 0 edges # Port 0 edges
graph.add_edge(pin_dict["wl1"], pin_dict["bl1"], self) 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["br1"], self)
# Port 1 is a write port, so its timing is not considered here. # 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils import utils
from tech import GDS,layer,parameter,drc from tech import GDS, layer
import logical_effort 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 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 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"] 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"]) pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_6t", GDS["unit"])
def __init__(self, name=""): def __init__(self, name=""):
# Ignore the name argument # 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") debug.info(2, "Create dummy bitcell")
self.width = dummy_bitcell.width self.width = dummy_bitcell.width
self.height = dummy_bitcell.height self.height = dummy_bitcell.height
self.pin_map = dummy_bitcell.pin_map 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils 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. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It 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. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] 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",
(width,height) = utils.get_libcell_size("dummy_cell_1rw_1r", GDS["unit"], layer["boundary"]) "INPUT", "INPUT", "POWER", "GROUND"]
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1rw_1r", GDS["unit"]) (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=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "dummy_cell_1rw_1r") bitcell_base.bitcell_base.__init__(self, "dummy_cell_1rw_1r")
debug.info(2, "Create dummy bitcell 1rw+1r object") debug.info(2, "Create dummy bitcell 1rw+1r object")
self.width = dummy_bitcell_1rw_1r.width 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.pin_map = dummy_bitcell_1rw_1r.pin_map
self.add_pin_types(self.type_list) 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import design
import debug import debug
import utils 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. A single bit cell which is forced to store a 0.
This module implements the single memory cell used in the design. It 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. """ the technology library. """
pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"] pin_names = ["bl0", "br0", "bl1", "br1", "wl0", "wl1", "vdd", "gnd"]
type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT", "INPUT", "INPUT", "POWER", "GROUND"] type_list = ["OUTPUT", "OUTPUT", "INPUT", "INPUT",
(width,height) = utils.get_libcell_size("dummy_cell_1w_1r", GDS["unit"], layer["boundary"]) "INPUT", "INPUT", "POWER", "GROUND"]
pin_map = utils.get_libcell_pins(pin_names, "dummy_cell_1w_1r", GDS["unit"]) (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=""): def __init__(self, name=""):
# Ignore the name argument # Ignore the name argument
design.design.__init__(self, "dummy_cell_1w_1r") bitcell_base.bitcell_base.__init__(self, "dummy_cell_1w_1r")
debug.info(2, "Create dummy bitcell 1w+1r object") debug.info(2, "Create dummy bitcell 1w+1r object")
self.width = dummy_bitcell_1w_1r.width 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.pin_map = dummy_bitcell_1w_1r.pin_map
self.add_pin_types(self.type_list) 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 wmask_zeroes = "0"*self.num_wmasks
if self.t_current == 0: if self.t_current == 0:
self.add_noop_all_ports("Idle cycle (no positive clock edge)", self.add_noop_all_ports("Idle cycle (no positive clock edge)")
inverse_address, data_zeros,wmask_zeroes)
self.add_write("W data 1 address {}".format(inverse_address), 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.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 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 # 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), 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.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.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)", self.add_noop_all_ports("Idle cycle (if read takes >1 cycle)")
inverse_address,data_zeros,wmask_zeroes)
self.add_write("W data 1 address {} to write value".format(self.probe_address), 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.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), 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 # 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), 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.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.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.add_noop_all_ports("Idle cycle (if read takes >1 cycle))")
self.probe_address,data_zeros,wmask_zeroes)
def get_available_port(self,get_read_port): def get_available_port(self,get_read_port):

View File

@ -6,6 +6,7 @@
# All rights reserved. # All rights reserved.
# #
import sys,re,shutil import sys,re,shutil
import copy
import collections import collections
from design import design from design import design
import debug import debug
@ -49,28 +50,19 @@ class functional(simulation):
self.create_graph() self.create_graph()
self.set_internal_spice_names() self.set_internal_spice_names()
self.initialize_wmask()
# Number of checks can be changed # Number of checks can be changed
self.num_cycles = 15 self.num_cycles = 15
# This is to have ordered keys for random selection # This is to have ordered keys for random selection
self.stored_words = collections.OrderedDict() self.stored_words = collections.OrderedDict()
self.write_check = []
self.read_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): def run(self, feasible_period=None):
if feasible_period: #period defaults to tech.py feasible period otherwise. if feasible_period: #period defaults to tech.py feasible period otherwise.
self.period = feasible_period self.period = feasible_period
# Generate a random sequence of reads and writes # Generate a random sequence of reads and writes
self.write_random_memory_sequence() self.create_random_memory_sequence()
# Run SPICE simulation # Run SPICE simulation
self.write_functional_stimulus() 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. # Check read values with written values. If the values do not match, return an error.
return self.check_stim_results() 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: if self.write_size:
rw_ops = ["noop", "write", "partial_write", "read"] rw_ops = ["noop", "write", "partial_write", "read"]
w_ops = ["noop", "write", "partial_write"] w_ops = ["noop", "write", "partial_write"]
@ -92,35 +102,45 @@ class functional(simulation):
rw_ops = ["noop", "write", "read"] rw_ops = ["noop", "write", "read"]
w_ops = ["noop", "write"] w_ops = ["noop", "write"]
r_ops = ["noop", "read"] r_ops = ["noop", "read"]
rw_read_din_data = "0"*self.word_size
check = 0
# First cycle idle # First cycle idle is always an idle cycle
comment = self.gen_cycle_comment("noop", "0"*self.word_size, "0"*self.addr_size, self.wmask, 0, self.t_current) 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, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks) self.add_noop_all_ports(comment)
# 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
# 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. # 1. Write all the write ports first to seed a bunch of locations.
# This will test the viablilty of the transistor sizing in the bitcell. for port in self.write_ports:
for port in self.all_ports: addr = self.gen_addr()
if port in self.write_ports: word = self.gen_data()
self.add_noop_one_port("0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks, port) comment = self.gen_cycle_comment("write", word, addr, "1"*self.num_wmasks, port, self.t_current)
else: self.add_write_one_port(comment, addr, word, "1"*self.num_wmasks, port)
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) self.stored_words[addr] = word
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]) # All other read-only ports are noops.
check += 1 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.cycle_times.append(self.t_current)
self.t_current += self.period 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 # 2. Read at least once. For multiport, it is important that one
# and random write masks (if applicable) # 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): for i in range(self.num_cycles):
w_addrs = [] w_addrs = []
for port in self.all_ports: for port in self.all_ports:
@ -132,63 +152,79 @@ class functional(simulation):
op = random.choice(r_ops) op = random.choice(r_ops)
if op == "noop": if op == "noop":
addr = "0"*self.addr_size self.add_noop_one_port(port)
word = "0"*self.word_size
wmask = "0" * self.num_wmasks
self.add_noop_one_port(addr, word, wmask, port)
elif op == "write": elif op == "write":
addr = self.gen_addr() addr = self.gen_addr()
word = self.gen_data()
# two ports cannot write to the same address # two ports cannot write to the same address
if addr in w_addrs: 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: else:
comment = self.gen_cycle_comment("write", word, addr, self.wmask, port, self.t_current) word = self.gen_data()
self.add_write_one_port(comment, addr, word, self.wmask, port) 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 self.stored_words[addr] = word
w_addrs.append(addr) w_addrs.append(addr)
elif op == "partial_write": elif op == "partial_write":
# write only to a word that's been written to # write only to a word that's been written to
(addr,old_word) = self.get_data() (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 # two ports cannot write to the same address
if addr in w_addrs: 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: 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) 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.add_write_one_port(comment, addr, word, wmask, port)
self.stored_words[addr] = new_word self.stored_words[addr] = new_word
w_addrs.append(addr) w_addrs.append(addr)
else: else:
(addr,word) = random.choice(list(self.stored_words.items())) (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: 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: else:
comment = self.gen_cycle_comment("read", word, addr, self.wmask, port, self.t_current) comment = self.gen_cycle_comment("read", word, addr, "0"*self.num_wmasks, port, self.t_current)
self.add_read_one_port(comment, addr, rw_read_din_data, "0"*self.num_wmasks, port) self.add_read_one_port(comment, addr, port)
self.write_check.append([word, "{0}{1}".format(self.dout_name,port), self.t_current+self.period, check]) self.add_read_check(word, port)
check += 1
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
# Last cycle idle needed to correctly measure the value on the second to last clock edge # 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) 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, "0"*self.addr_size, "0"*self.word_size, "0"*self.num_wmasks) 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): def read_stim_results(self):
# Extrat dout values from spice timing.lis # Extract dout values from spice timing.lis
for (word, dout_port, eo_period, check) in self.write_check: for (word, dout_port, eo_period, check) in self.read_check:
sp_read_value = "" sp_read_value = ""
for bit in range(self.word_size): for bit in range(self.word_size):
value = parse_spice_list("timing", "v{0}.{1}ck{2}".format(dout_port.lower(),bit,check)) 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) self.v_high)
return (0, error) 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") return (1, "SUCCESS")
def check_stim_results(self): def check_stim_results(self):
for i in range(len(self.write_check)): for i in range(len(self.read_check)):
if self.write_check[i][0] != self.read_check[i][0]: 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_check[i][1], 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.read_check[i][0],
self.write_check[i][0], int((self.read_results[i][2]-self.period)/self.period),
int((self.read_check[i][2]-self.period)/self.period), self.read_results[i][2])
self.read_check[i][2])
return(0, error) return(0, error)
return(1, "SUCCESS") return(1, "SUCCESS")
@ -359,7 +395,7 @@ class functional(simulation):
# Generate dout value measurements # Generate dout value measurements
self.sf.write("\n * Generation of dout measurements\n") 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_intital = eo_period - 0.01*self.period
t_final = eo_period + 0.01*self.period t_final = eo_period + 0.01*self.period
for bit in range(self.word_size): 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), port_info=(len(self.all_ports),self.write_ports,self.read_ports),
abits=self.addr_size, abits=self.addr_size,
dbits=self.word_size) dbits=self.word_size)
debug.check(len(self.sram.pins) == len(self.pins), "Number of pins generated for characterization \ debug.check(len(self.sram.pins) == len(self.pins),
do match pins of SRAM\nsram.pins = {0}\npin_names = {1}".format(self.sram.pins,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. #This is TODO once multiport control has been finalized.
#self.control_name = "CSB" #self.control_name = "CSB"
@ -71,13 +73,18 @@ class simulation():
self.t_current = 0 self.t_current = 0
# control signals: only one cs_b for entire multiported sram, one we_b for each write port # 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.csb_values = {port:[] for port in self.all_ports}
self.web_values = [[] for port in self.readwrite_ports] self.web_values = {port:[] for port in self.readwrite_ports}
# Three dimensional list to handle each addr and data bits for wach port over the number of checks # Raw values added as a bit vector
self.addr_values = [[[] for bit in range(self.addr_size)] for port in self.all_ports] self.addr_value = {port:[] for port in self.all_ports}
self.data_values = [[[] for bit in range(self.word_size)] for port in self.write_ports] self.data_value = {port:[] for port in self.write_ports}
self.wmask_values = [[[] for bit in range(self.num_wmasks)] 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 # For generating comments in SPICE stimulus
self.cycle_comments = [] self.cycle_comments = []
@ -105,7 +112,8 @@ class simulation():
def add_data(self, data, port): def add_data(self, data, port):
""" Add the array of data values """ """ Add the array of data values """
debug.check(len(data)==self.word_size, "Invalid data word size.") debug.check(len(data)==self.word_size, "Invalid data word size.")
self.data_value[port].append(data)
bit = self.word_size - 1 bit = self.word_size - 1
for c in data: for c in data:
if c=="0": if c=="0":
@ -116,10 +124,12 @@ class simulation():
debug.error("Non-binary data string",1) debug.error("Non-binary data string",1)
bit -= 1 bit -= 1
def add_address(self, address, port): def add_address(self, address, port):
""" Add the array of address values """ """ Add the array of address values """
debug.check(len(address)==self.addr_size, "Invalid address size.") debug.check(len(address)==self.addr_size, "Invalid address size.")
self.addr_value[port].append(address)
bit = self.addr_size - 1 bit = self.addr_size - 1
for c in address: for c in address:
if c=="0": if c=="0":
@ -130,10 +140,12 @@ class simulation():
debug.error("Non-binary address string",1) debug.error("Non-binary address string",1)
bit -= 1 bit -= 1
def add_wmask(self, wmask, port): def add_wmask(self, wmask, port):
""" Add the array of address values """ """ Add the array of address values """
debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.") debug.check(len(wmask) == self.num_wmasks, "Invalid wmask size.")
self.wmask_value[port].append(wmask)
bit = self.num_wmasks - 1 bit = self.num_wmasks - 1
for c in wmask: for c in wmask:
if c == "0": if c == "0":
@ -143,10 +155,13 @@ class simulation():
else: else:
debug.error("Non-binary wmask string", 1) debug.error("Non-binary wmask string", 1)
bit -= 1 bit -= 1
def add_write(self, comment, address, data, wmask, port): def add_write(self, comment, address, data, wmask, port):
""" Add the control values for a write cycle. """ """ 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) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment) self.append_cycle_comment(port, comment)
@ -159,16 +174,16 @@ class simulation():
self.add_address(address,port) self.add_address(address,port)
self.add_wmask(wmask,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. #Add noops to all other ports.
for unselected_port in self.all_ports: for unselected_port in self.all_ports:
if unselected_port != port: 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. """ """ 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) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.append_cycle_comment(port, comment) self.append_cycle_comment(port, comment)
@ -176,21 +191,26 @@ class simulation():
self.cycle_times.append(self.t_current) self.cycle_times.append(self.t_current)
self.t_current += self.period self.t_current += self.period
self.add_control_one_port(port, "read") 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) self.add_address(address, port)
#This value is hard coded here. Possibly change to member variable or set in add_noop_one_port # If the port is also a readwrite then add
noop_data = "0"*self.word_size # 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. #Add noops to all other ports.
for unselected_port in self.all_ports: for unselected_port in self.all_ports:
if unselected_port != port: 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. """ """ Add the control values for a noop to all ports. """
debug.info(2, comment) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
@ -200,39 +220,64 @@ class simulation():
self.t_current += self.period self.t_current += self.period
for port in self.all_ports: 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): def add_write_one_port(self, comment, address, data, wmask, port):
""" Add the control values for a write cycle. Does not increment the period. """ """ 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) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "write") self.add_control_one_port(port, "write")
self.add_data(data,port) self.add_data(data, port)
self.add_address(address,port) self.add_address(address, port)
self.add_wmask(wmask,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. """ """ 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) debug.info(2, comment)
self.fn_cycle_comments.append(comment) self.fn_cycle_comments.append(comment)
self.add_control_one_port(port, "read") 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) 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. """ """ Add the control values for a noop to a single port. Does not increment the period. """
self.add_control_one_port(port, "noop") 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: if port in self.write_ports:
self.add_data(data,port) try:
self.add_wmask(wmask,port) self.add_data(self.data_value[port][-1], port)
self.add_address(address, 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): def append_cycle_comment(self, port, comment):
"""Add comment to list to be printed in stimulus file""" """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 = "{0:.2f} ns:".format(self.t_current)
time_spacing = len(time)+6 time_spacing = len(time)+6
self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times), self.cycle_comments.append("Cycle {0:<6d} Port {1:<6} {2:<{3}}: {4}".format(len(self.cycle_times),
port, port,
time, time,
time_spacing, time_spacing,
comment)) comment))
def gen_cycle_comment(self, op, word, addr, wmask, port, t_current): def gen_cycle_comment(self, op, word, addr, wmask, port, t_current):
if op == "noop": if op == "noop":
comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period), comment = "\tIdle during cycle {0} ({1}ns - {2}ns)".format(int(t_current/self.period),
t_current, t_current,
t_current+self.period) t_current+self.period)
elif op == "write": elif op == "write":
comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word, comment = "\tWriting {0} to address {1} (from port {2}) during cycle {3} ({4}ns - {5}ns)".format(word,
addr, addr,

View File

@ -96,22 +96,6 @@ class stimuli():
self.sf.write(".ENDS test_{0}\n\n".format(buffer_name)) 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): def gen_pulse(self, sig_name, v1, v2, offset, period, t_rise, t_fall):
""" """
@ -276,9 +260,6 @@ class stimuli():
""" Writes supply voltage statements """ """ Writes supply voltage statements """
gnd_node_name = "0" gnd_node_name = "0"
self.sf.write("V{0} {0} {1} {2}\n".format(self.vdd_name, gnd_node_name, self.voltage)) 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. #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") 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] frm = inspect.stack()[1]
mod = inspect.getmodule(frm[0]) mod = inspect.getmodule(frm[0])
# classname = frm.f_globals['__name__'] # classname = frm.f_globals['__name__']
if mod.__name__ == None: if mod.__name__ is None:
class_name = "" class_name = ""
else: else:
class_name = mod.__name__ class_name = mod.__name__

View File

@ -3,11 +3,11 @@ num_words = 128
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 5.0 ] supply_voltages = [5.0]
temperatures = [ 25 ] temperatures = [25]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -14,7 +14,9 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -10,5 +10,7 @@ route_supplies = True
check_lvsdrc = True check_lvsdrc = True
output_path = "temp" 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 check_lvsdrc = True
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -7,7 +7,9 @@ supply_voltages = [ 5.0 ]
temperatures = [ 25 ] temperatures = [ 25 ]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" lvs_name = "netgen"

View File

@ -3,11 +3,13 @@ num_words = 256
tech_name = "scn4m_subm" tech_name = "scn4m_subm"
process_corners = ["TT"] process_corners = ["TT"]
supply_voltages = [ 3.3 ] supply_voltages = [3.3]
temperatures = [ 25 ] temperatures = [25]
output_path = "temp" 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" drc_name = "magic"
lvs_name = "netgen" 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. corner, but should probably be extended.
""" """
import sys,os import sys
import datetime
import re
import importlib
from globals import * from globals import *
(OPTS, args) = parse_args() (OPTS, args) = parse_args()

View File

@ -42,7 +42,7 @@ class control_logic(design.design):
self.enable_delay_chain_resizing = False self.enable_delay_chain_resizing = False
self.inv_parasitic_delay = logical_effort.logical_effort.pinv self.inv_parasitic_delay = logical_effort.logical_effort.pinv
#Determines how much larger the sen delay should be. Accounts for possible error in model. # Determines how much larger the sen delay should be. Accounts for possible error in model.
self.wl_timing_tolerance = 1 self.wl_timing_tolerance = 1
self.wl_stage_efforts = None self.wl_stage_efforts = None
self.sen_stage_efforts = None self.sen_stage_efforts = None
@ -201,7 +201,7 @@ class control_logic(design.design):
def get_heuristic_delay_chain_size(self): 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 """ """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 delay_fanout = 3 # This can be anything >=3
# Model poorly captures delay of the column mux. Be pessismistic for column mux # Model poorly captures delay of the column mux. Be pessismistic for column mux
if self.words_per_row >= 2: if self.words_per_row >= 2:
@ -209,8 +209,8 @@ class control_logic(design.design):
else: else:
delay_stages = 2 delay_stages = 2
#Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference # Read ports have a shorter s_en delay. The model is not accurate enough to catch this difference
#on certain sram configs. # on certain sram configs.
if self.port_type == "r": if self.port_type == "r":
delay_stages+=2 delay_stages+=2
@ -226,7 +226,7 @@ class control_logic(design.design):
def does_sen_rise_fall_timing_match(self): def does_sen_rise_fall_timing_match(self):
"""Compare the relative rise/fall delays of the sense amp enable and wordline""" """Compare the relative rise/fall delays of the sense amp enable and wordline"""
self.set_sen_wl_delays() self.set_sen_wl_delays()
#This is not necessarily more reliable than total delay in some cases. # This is not necessarily more reliable than total delay in some cases.
if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or if (self.wl_delay_rise*self.wl_timing_tolerance >= self.sen_delay_rise or
self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall): self.wl_delay_fall*self.wl_timing_tolerance >= self.sen_delay_fall):
return False return False
@ -236,8 +236,9 @@ class control_logic(design.design):
def does_sen_total_timing_match(self): def does_sen_total_timing_match(self):
"""Compare the total delays of the sense amp enable and wordline""" """Compare the total delays of the sense amp enable and wordline"""
self.set_sen_wl_delays() 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 # The sen delay must always be bigger than than the wl
#a re-size is warranted. # 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: if self.wl_delay*self.wl_timing_tolerance >= self.sen_delay:
return False return False
else: else:
@ -250,14 +251,14 @@ class control_logic(design.design):
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
delay_fanout = 3 # This can be anything >=2 delay_fanout = 3 # This can be anything >=2
#The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
#inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay) required_delay = self.wl_delay*self.wl_timing_tolerance - (self.sen_delay-previous_delay_chain_delay)
debug.check(required_delay > 0, "Cannot size delay chain to have negative delay") debug.check(required_delay > 0, "Cannot size delay chain to have negative delay")
delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay)) delay_stages = ceil(required_delay/(delay_fanout+1+self.inv_parasitic_delay))
if delay_stages%2 == 1: #force an even number of stages. if delay_stages%2 == 1: #force an even number of stages.
delay_stages+=1 delay_stages+=1
#Fanout can be varied as well but is a little more complicated but potentially optimal. # Fanout can be varied as well but is a little more complicated but potentially optimal.
debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay)) debug.info(1, "Setting delay chain to {} stages with {} fanout to match {} delay".format(delay_stages, delay_fanout, required_delay))
return (delay_stages, delay_fanout) return (delay_stages, delay_fanout)
@ -268,16 +269,16 @@ class control_logic(design.design):
debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay)) debug.info(2, "Previous delay chain produced {} delay units".format(previous_delay_chain_delay))
fanout_rise = fanout_fall = 2 # This can be anything >=2 fanout_rise = fanout_fall = 2 # This can be anything >=2
#The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each # The delay chain uses minimum sized inverters. There are (fanout+1)*stages inverters and each
#inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value # inverter adds 1 unit of delay (due to minimum size). This also depends on the pinv value
required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (self.sen_delay_fall-previous_delay_chain_delay/2) required_delay_fall = self.wl_delay_fall*self.wl_timing_tolerance - (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) 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)) debug.info(2,"Required delays from chain: fall={}, rise={}".format(required_delay_fall,required_delay_rise))
#If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic. # If the fanout is different between rise/fall by this amount. Stage algorithm is made more pessimistic.
WARNING_FANOUT_DIFF = 5 WARNING_FANOUT_DIFF = 5
stages_close = False stages_close = False
#The stages need to be equal (or at least a even number of stages with matching rise/fall delays) # The stages need to be equal (or at least a even number of stages with matching rise/fall delays)
while True: while True:
stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall) stages_fall = self.calculate_stages_with_fixed_fanout(required_delay_fall,fanout_fall)
stages_rise = self.calculate_stages_with_fixed_fanout(required_delay_rise,fanout_rise) 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_rise = safe_fanout_rise
fanout_fall = safe_fanout_fall fanout_fall = safe_fanout_fall
break break
#There should also be a condition to make sure the fanout does not get too large. # There should also be a condition to make sure the fanout does not get too large.
#Otherwise, increase the fanout of delay with the most stages, calculate new stages # Otherwise, increase the fanout of delay with the most stages, calculate new stages
elif stages_fall>stages_rise: elif stages_fall>stages_rise:
fanout_fall+=1 fanout_fall+=1
else: else:
@ -304,13 +305,13 @@ class control_logic(design.design):
total_stages = max(stages_fall,stages_rise)*2 total_stages = max(stages_fall,stages_rise)*2
debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall)) debug.info(1, "New Delay chain: stages={}, fanout_rise={}, fanout_fall={}".format(total_stages, fanout_rise, fanout_fall))
#Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage. # Creates interleaved fanout list of rise/fall delays. Assumes fall is the first stage.
stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)] stage_list = [fanout_fall if i%2==0 else fanout_rise for i in range(total_stages)]
return stage_list return stage_list
def calculate_stages_with_fixed_fanout(self, required_delay, fanout): def calculate_stages_with_fixed_fanout(self, required_delay, fanout):
from math import ceil from math import ceil
#Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay # Delay being negative is not an error. It implies that any amount of stages would have a negative effect on the overall delay
if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0). if required_delay <= 3+self.inv_parasitic_delay: #3 is the minimum delay per stage (with pinv=0).
return 1 return 1
delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay)) delay_stages = ceil(required_delay/(fanout+1+self.inv_parasitic_delay))
@ -421,7 +422,7 @@ class control_logic(design.design):
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "w"): if (self.port_type == "rw") or (self.port_type == "w"):
self.place_rbl_delay_row(row) self.place_rbl_delay_row(row)
row += 1 row += 1
if (self.port_type == "rw") or (self.port_type == "r"): if (self.port_type == "rw") or (self.port_type == "r"):
self.place_sen_row(row) self.place_sen_row(row)
row += 1 row += 1
@ -462,6 +463,7 @@ class control_logic(design.design):
""" Create the replica bitline """ """ Create the replica bitline """
self.delay_inst=self.add_inst(name="delay_chain", self.delay_inst=self.add_inst(name="delay_chain",
mod=self.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"]) self.connect_inst(["rbl_bl", "rbl_bl_delay", "vdd", "gnd"])
def place_delay(self,row): def place_delay(self,row):
@ -612,6 +614,8 @@ class control_logic(design.design):
def create_pen_row(self): def create_pen_row(self):
self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar", self.p_en_bar_nand_inst=self.add_inst(name="nand_p_en_bar",
mod=self.nand2) 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.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", 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 # GATE FOR S_EN
self.s_en_gate_inst = self.add_inst(name="buf_s_en_and", self.s_en_gate_inst = self.add_inst(name="buf_s_en_and",
mod=self.sen_and3) 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"]) 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") self.connect_output(self.s_en_gate_inst, "Z", "s_en")
def create_rbl_delay_row(self): def create_rbl_delay_row(self):
self.rbl_bl_delay_inv_inst = self.add_inst(name="rbl_bl_delay_inv", 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"]) rbl_map = zip(["A"], ["rbl_bl_delay"])
self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets) self.connect_vertical_bus(rbl_map, self.rbl_bl_delay_inv_inst, self.rail_offsets)
def create_wen_row(self): def create_wen_row(self):
# input: we (or cs) output: w_en # input: we (or cs) output: w_en
@ -709,6 +716,7 @@ class control_logic(design.design):
# GATE THE W_EN # GATE THE W_EN
self.w_en_gate_inst = self.add_inst(name="w_en_and", self.w_en_gate_inst = self.add_inst(name="w_en_and",
mod=self.wen_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"]) 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 a Liberty (.lib) file for timing analysis/optimization
""" """
import sys,os import sys
import datetime import datetime
import re import globals as g
import importlib
from globals import *
(OPTS, args) = parse_args() (OPTS, args) = g.parse_args()
# Check that we are left with a single configuration file as argument. # Check that we are left with a single configuration file as argument.
if len(args) != 1: if len(args) != 1:
print(USAGE) print(g.USAGE)
sys.exit(2) sys.exit(2)
# These depend on arguments, so don't load them until now. # These depend on arguments, so don't load them until now.
import debug 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 # Only print banner here so it's not in unit tests
print_banner() g.print_banner()
# Keep track of running stats # Keep track of running stats
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
print_time("Start",start_time) g.print_time("Start", start_time)
# Output info about this run # Output info about this run
report_status() g.report_status()
from sram_config import sram_config from sram_config import sram_config
@ -54,15 +52,16 @@ c = sram_config(word_size=OPTS.word_size,
write_size=OPTS.write_size) write_size=OPTS.write_size)
debug.print_raw("Words per row: {}".format(c.words_per_row)) 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 # Only output lef/gds if back-end
if not OPTS.netlist_only: 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: ") debug.print_raw("Output files are: ")
for path in output_files: for path in output_files:
debug.print_raw(path) debug.print_raw(path)
@ -74,7 +73,7 @@ s = sram(sram_config=c,
s.save() s.save()
# Delete temp files etc. # Delete temp files etc.
end_openram() g.end_openram()
print_time("End",datetime.datetime.now(), start_time) g.print_time("End", datetime.datetime.now(), start_time)

View File

@ -6,13 +6,13 @@
# All rights reserved. # All rights reserved.
# #
import optparse import optparse
import getpass import getpass
import os import os
#import sram_config
class options(optparse.Values): 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. that is the sole required command-line positional argument for openram.py.
""" """
@ -39,15 +39,16 @@ class options(optparse.Values):
process_corners = "" process_corners = ""
# Size parameters must be specified by user in config file. # Size parameters must be specified by user in config file.
#num_words = 0 # num_words = 0
#word_size = 0 # word_size = 0
# You can manually specify banks, but it is better to auto-detect it. # You can manually specify banks, but it is better to auto-detect it.
num_banks = 1 num_banks = 1
################### ###################
# Optimization options # 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 # Allow manual adjustment of the delay chain over automatic
use_tech_delay_chain_size = False use_tech_delay_chain_size = False
@ -65,7 +66,8 @@ class options(optparse.Values):
openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP")) openram_temp = os.path.abspath(os.environ.get("OPENRAM_TMP"))
except: except:
# Else use a unique temporary directory # 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 # This is the verbosity level to control debug information. 0 is none, 1
# is minimal, etc. # is minimal, etc.
debug_level = 0 debug_level = 0
@ -100,7 +102,8 @@ class options(optparse.Values):
drc_name = "" drc_name = ""
lvs_name = "" lvs_name = ""
pex_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 drc_exe = None
lvs_exe = None lvs_exe = None
pex_exe = None pex_exe = None
@ -113,15 +116,14 @@ class options(optparse.Values):
output_path = "." output_path = "."
# Define the output file base name # Define the output file base name
output_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 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 purge_temp = True
###################
# These are the default modules that can be over-riden # These are the default modules that can be over-riden
###################
bank_select = "bank_select" bank_select = "bank_select"
bitcell_array = "bitcell_array" bitcell_array = "bitcell_array"
bitcell = "bitcell" bitcell = "bitcell"

View File

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

View File

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

View File

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

View File

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

View File

@ -8,14 +8,16 @@
import contact import contact
import design import design
import debug import debug
from tech import drc, parameter, spice from tech import drc
from vector import vector from vector import vector
from globals import OPTS from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pgate(design.design): 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): def __init__(self, name, height=None):
@ -29,78 +31,85 @@ class pgate(design.design):
self.height = b.height self.height = b.height
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
self.add_boundary() self.add_boundary()
self.DRC_LVS() self.DRC_LVS()
def create_netlist(self): def create_netlist(self):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_netlist.",-1) debug.error("Must over-ride create_netlist.", -1)
def create_layout(self): def create_layout(self):
""" Pure virtual function """ """ Pure virtual function """
debug.error("Must over-ride create_layout.",-1) debug.error("Must over-ride create_layout.", -1)
def connect_pin_to_rail(self,inst,pin,supply): def connect_pin_to_rail(self, inst, pin, supply):
""" Connects a ptx pin to a supply rail. """ """ Connects a ptx pin to a supply rail. """
source_pin = inst.get_pin(pin) source_pin = inst.get_pin(pin)
supply_pin = self.get_pin(supply) supply_pin = self.get_pin(supply)
if supply_pin.overlaps(source_pin): if supply_pin.overlaps(source_pin):
return return
if supply=="gnd": if supply == "gnd":
height=supply_pin.by()-source_pin.by() height = supply_pin.by() - source_pin.by()
elif supply=="vdd": elif supply == "vdd":
height=supply_pin.uy()-source_pin.by() height = supply_pin.uy() - source_pin.by()
else: else:
debug.error("Invalid supply name.",-1) debug.error("Invalid supply name.", -1)
if abs(height)>0: if abs(height) > 0:
self.add_rect(layer="metal1", self.add_rect(layer="metal1",
offset=source_pin.ll(), offset=source_pin.ll(),
height=height, height=height,
width=source_pin.width()) width=source_pin.width())
def route_input_gate(self, pmos_inst, nmos_inst, ypos, name, position="left", rotate=False): 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") nmos_gate_pin = nmos_inst.get_pin("G")
pmos_gate_pin = pmos_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! # 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 # 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) 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) pmos_gate_pos = vector(nmos_gate_pos.x, pmos_gate_pin.bc().y)
self.add_path("poly",[nmos_gate_pos,pmos_gate_pos]) self.add_path("poly", [nmos_gate_pos, pmos_gate_pos])
# Add the via to the cell midpoint along the gate # 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: if rotate:
contact_width = contact.poly.height contact_width = contact.poly.height
contact_m1_width = contact.poly.second_layer_height contact_m1_width = contact.poly.second_layer_height
contact_m1_height = contact.poly.second_layer_width contact_m1_height = contact.poly.second_layer_width
directions = ("H","V") directions = ("H", "V")
else: else:
contact_width = contact.poly.width contact_width = contact.poly.width
contact_m1_width = contact.poly.second_layer_width contact_m1_width = contact.poly.second_layer_width
contact_m1_height = contact.poly.second_layer_height contact_m1_height = contact.poly.second_layer_height
directions = ("V","H") directions = ("V", "H")
if position=="center": if position == "center":
contact_offset = left_gate_offset + vector(0.5*self.poly_width, 0) contact_offset = left_gate_offset \
elif position=="farleft": + vector(0.5 * self.poly_width, 0)
contact_offset = left_gate_offset - vector(0.5*contact.poly.width, 0) elif position == "farleft":
elif position=="left": contact_offset = left_gate_offset \
contact_offset = left_gate_offset - vector(0.5*contact_width - 0.5*self.poly_width, 0) - vector(0.5 * contact.poly.width, 0)
elif position=="right": elif position == "left":
contact_offset = left_gate_offset + vector(0.5*contact.width + 0.5*self.poly_width, 0) 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: else:
debug.error("Invalid contact placement option.", -1) debug.error("Invalid contact placement option.", -1)
@ -110,29 +119,26 @@ class pgate(design.design):
offset=contact_offset, offset=contact_offset,
directions=directions) 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, self.add_layout_pin_rect_center(text=name,
layer="metal1", layer="metal1",
offset=contact_offset, offset=contact_offset,
width=contact_m1_width, width=contact_m1_width,
height=contact_m1_height) height=contact_m1_height)
# This is to ensure that the contact is
# This is to ensure that the contact is connected to the gate # connected to the gate
mid_point = contact_offset.scale(0.5,1)+left_gate_offset.scale(0.5,0) mid_point = contact_offset.scale(0.5, 1) \
+ left_gate_offset.scale(0.5, 0)
self.add_rect_center(layer="poly", self.add_rect_center(layer="poly",
offset=mid_point, offset=mid_point,
height=contact.poly.first_layer_width, 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): def extend_wells(self, middle_position):
""" Extend the n/p wells to cover whole cell """ """ Extend the n/p wells to cover whole cell """
# Add a rail width to extend the well to the top of the rail # 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 self.nwell_position = middle_position
nwell_height = max_y_offset - middle_position.y nwell_height = max_y_offset - middle_position.y
if drc("has_nwell"): if drc("has_nwell"):
@ -145,8 +151,8 @@ class pgate(design.design):
width=self.well_width, width=self.well_width,
height=nwell_height) height=nwell_height)
pwell_position = vector(0,-0.5*self.m1_width) pwell_position = vector(0, -0.5 * self.m1_width)
pwell_height = middle_position.y-pwell_position.y pwell_height = middle_position.y - pwell_position.y
if drc("has_pwell"): if drc("has_pwell"):
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=pwell_position, offset=pwell_position,
@ -163,38 +169,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1") layer_stack = ("active", "contact", "metal1")
# To the right a spacing away from the pmos right active edge # 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") contact_xoffset = pmos_pos.x + pmos.active_width \
# Must be at least an well enclosure of active down from the top of the well + 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. # 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, 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) contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact in x and y # Offset by half a contact in x and y
contact_offset += vector(0.5*pmos.active_contact.first_layer_width, contact_offset += vector(0.5 * pmos.active_contact.first_layer_width,
0.5*pmos.active_contact.first_layer_height) 0.5 * pmos.active_contact.first_layer_height)
self.nwell_contact=self.add_via_center(layers=layer_stack, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
directions=("H","V"), directions=("H", "V"),
implant_type="n", implant_type="n",
well_type="n") well_type="n")
self.add_rect_center(layer="metal1", 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, width=self.nwell_contact.mod.second_layer_width,
height=self.height - contact_offset.y) height=self.height - contact_offset.y)
# Now add the full active and implant for the PMOS # Now add the full active and implant for the PMOS
#active_offset = pmos_pos + vector(pmos.active_width,0) # active_offset = pmos_pos + vector(pmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed # This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active", # self.add_rect(layer="active",
# offset=active_offset, # offset=active_offset,
# width=pmos.active_contact.width, # width=pmos.active_contact.width,
# height=pmos.active_height) # 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_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) # implant_offset = active_offset + vector(implant_spacing,0) \
# implant_width = pmos.active_contact.width + 2*self.implant_enclose_active # - 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 # implant_height = pmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="nimplant", # self.add_rect(layer="nimplant",
# offset=implant_offset, # offset=implant_offset,
@ -208,39 +221,45 @@ class pgate(design.design):
layer_stack = ("active", "contact", "metal1") 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 # 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") contact_xoffset = nmos_pos.x + nmos.active_width \
# Must be at least an well enclosure of active up from the bottom of the well + 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, 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) contact_offset = vector(contact_xoffset, contact_yoffset)
# Offset by half a contact # Offset by half a contact
contact_offset += vector(0.5*nmos.active_contact.first_layer_width, contact_offset += vector(0.5 * nmos.active_contact.first_layer_width,
0.5*nmos.active_contact.first_layer_height) 0.5 * nmos.active_contact.first_layer_height)
self.pwell_contact=self.add_via_center(layers=layer_stack, self.pwell_contact= self.add_via_center(layers=layer_stack,
offset=contact_offset, offset=contact_offset,
directions=("H","V"), directions=("H", "V"),
implant_type="p", implant_type="p",
well_type="p") well_type="p")
self.add_rect_center(layer="metal1", self.add_rect_center(layer="metal1",
offset=contact_offset.scale(1,0.5), offset=contact_offset.scale(1,0.5),
width=self.pwell_contact.mod.second_layer_width, width=self.pwell_contact.mod.second_layer_width,
height=contact_offset.y) height=contact_offset.y)
# Now add the full active and implant for the NMOS # Now add the full active and implant for the NMOS
# active_offset = nmos_pos + vector(nmos.active_width,0) # active_offset = nmos_pos + vector(nmos.active_width,0)
# This might be needed if the spacing between the actives is not satisifed # This might be needed if the spacing between the actives
# is not satisifed
# self.add_rect(layer="active", # self.add_rect(layer="active",
# offset=active_offset, # offset=active_offset,
# width=nmos.active_contact.width, # width=nmos.active_contact.width,
# height=nmos.active_height) # height=nmos.active_height)
# implant_spacing = self.implant_space+self.implant_enclose_active # implant_spacing = self.implant_space+self.implant_enclose_active
# implant_offset = active_offset + vector(implant_spacing,0) - vector(0,self.implant_enclose_active) # implant_offset = active_offset + vector(implant_spacing,0) \
# implant_width = nmos.active_contact.width + 2*self.implant_enclose_active # - 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 # implant_height = nmos.active_height + 2*self.implant_enclose_active
# self.add_rect(layer="pimplant", # self.add_rect(layer="pimplant",
# offset=implant_offset, # offset=implant_offset,

View File

@ -16,6 +16,7 @@ from utils import round_to_grid
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pinv(pgate.pgate): class pinv(pgate.pgate):
""" """
Pinv generates gds of a parametrically sized inverter. The Pinv generates gds of a parametrically sized inverter. The
@ -28,12 +29,14 @@ class pinv(pgate.pgate):
def __init__(self, name, size=1, beta=parameter["beta"], height=None, route_output=True): def __init__(self, name, size=1, beta=parameter["beta"], height=None, 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.add_comment("size: {}".format(size))
self.size = size self.size = size
self.nmos_size = size self.nmos_size = size
self.pmos_size = beta*size self.pmos_size = beta * size
self.beta = beta self.beta = beta
self.route_output = False self.route_output = False
@ -44,7 +47,7 @@ class pinv(pgate.pgate):
self.add_pins() self.add_pins()
self.determine_tx_mults() self.determine_tx_mults()
self.add_ptx() self.add_ptx()
self.create_ptx() self.create_ptx()
def create_layout(self): def create_layout(self):
""" Calls all functions related to the generation of the layout """ """ Calls all functions related to the generation of the layout """
@ -54,7 +57,11 @@ class pinv(pgate.pgate):
self.add_well_contacts() self.add_well_contacts()
self.extend_wells(self.well_pos) self.extend_wells(self.well_pos)
self.connect_rails() 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() self.route_outputs()
def add_pins(self): def add_pins(self):
@ -63,7 +70,6 @@ class pinv(pgate.pgate):
dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"] dir_list = ["INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
def determine_tx_mults(self): def determine_tx_mults(self):
""" """
Determines the number of fingers needed to achieve the size within 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... # This may make the result differ when the layout is created...
if OPTS.netlist_only: if OPTS.netlist_only:
self.tx_mults = 1 self.tx_mults = 1
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
return return
# Do a quick sanity check and bail if unlikely feasible height # 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? # Sanity check. can we make an inverter in the height
# Assume we need 3 metal 1 pitches (2 power rails, one between the tx for the drain) # 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 # plus the tx height
nmos = factory.create(module_type="ptx", tx_type="nmos") 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 tx_height = nmos.poly_height + pmos.poly_height
# rotated m1 pitch or poly to active spacing # rotated m1 pitch or poly to active spacing
min_channel = max(contact.poly.width + self.m1_space, min_channel = max(contact.poly.width + self.m1_space,
contact.poly.width + 2*drc("poly_to_active")) 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 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 # 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) drc("poly_extend_active"), self.poly_space)
total_height = tx_height + min_channel + 2*self.top_bottom_space total_height = tx_height + min_channel + 2 * self.top_bottom_space
debug.check(self.height> total_height,"Cell height {0} too small for simple min height {1}.".format(self.height,total_height)) 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 # Determine the height left to the transistors to determine
tx_height_available = self.height - min_channel - 2*self.top_bottom_space # the number of fingers
# Divide the height in half. Could divide proportional to beta, but this makes tx_height_available = self.height - min_channel - 2 * self.top_bottom_space
# connecting wells of multiple cells easier. # 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 # Subtract the poly space under the rail of the tx
nmos_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") 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, debug.info(2,
nmos_height_available, "Height avail {0:.4f} PMOS {1:.4f} NMOS {2:.4f}".format(tx_height_available,
pmos_height_available)) nmos_height_available,
pmos_height_available))
# Determine the number of mults for each to fit width into available space # Determine the number of mults for each to fit width
self.nmos_width = self.nmos_size*drc("minwidth_tx") # into available space
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
nmos_required_mults = max(int(ceil(self.nmos_width/nmos_height_available)),1) self.pmos_width = self.pmos_size * drc("minwidth_tx")
pmos_required_mults = max(int(ceil(self.pmos_width/pmos_height_available)),1) 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 # The mults must be the same for easy connection of poly
self.tx_mults = max(nmos_required_mults, pmos_required_mults) self.tx_mults = max(nmos_required_mults, pmos_required_mults)
# Recompute each mult width and check it isn't too small # Recompute each mult width and check it isn't too small
# This could happen if the height is narrow and the size is small # This could happen if the height is narrow and the size is small
# User should pick a bigger size to fix it... # 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 # We also need to round the width to the grid or we will end up
# mismatch errors when fingers are not a grid length and get rounded in the offset geometry. # 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) 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) 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): 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 \ self.well_width = self.pmos.active_width + self.pmos.active_contact.width \
+ drc("active_to_body_active") + 2*drc("well_enclosure_active") + drc("active_to_body_active") + 2*drc("well_enclosure_active")
self.width = self.well_width 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): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
@ -162,58 +179,57 @@ class pinv(pgate.pgate):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS netlist. Create the PMOS and NMOS netlist.
""" """
self.pmos_inst=self.add_inst(name="pinv_pmos", self.pmos_inst = self.add_inst(name="pinv_pmos",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "A", "vdd", "vdd"]) self.connect_inst(["Z", "A", "vdd", "vdd"])
self.nmos_inst=self.add_inst(name="pinv_nmos", self.nmos_inst = self.add_inst(name="pinv_nmos",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
# place PMOS so it is half a poly spacing down from the top # place PMOS so it is half a poly spacing down from the top
self.pmos_pos = self.pmos.active_offset.scale(1,0) \ self.pmos_pos = self.pmos.active_offset.scale(1, 0) \
+ vector(0, self.height-self.pmos.active_height-self.top_bottom_space) + vector(0,
self.height - self.pmos.active_height - self.top_bottom_space)
self.pmos_inst.place(self.pmos_pos) self.pmos_inst.place(self.pmos_pos)
# place NMOS so that it is half a poly spacing up from the bottom # 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) self.nmos_inst.place(self.nmos_pos)
# Output position will be in between the PMOS and NMOS drains # Output position will be in between the PMOS and NMOS drains
pmos_drain_pos = self.pmos_inst.get_pin("D").ll() pmos_drain_pos = self.pmos_inst.get_pin("D").ll()
nmos_drain_pos = self.nmos_inst.get_pin("D").ul() 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)) 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())
# This will help with the wells
self.well_pos = vector(0, self.nmos_inst.uy())
def route_outputs(self): 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 # Get the drain pins
nmos_drain_pin = self.nmos_inst.get_pin("D") 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 # Pick point at right most of NMOS and connect down to PMOS
nmos_drain_pos = nmos_drain_pin.bc() nmos_drain_pos = nmos_drain_pin.bc()
pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y) pmos_drain_pos = vector(nmos_drain_pos.x, pmos_drain_pin.uc().y)
self.add_path("metal1",[nmos_drain_pos,pmos_drain_pos]) self.add_path("metal1", [nmos_drain_pos, pmos_drain_pos])
# Remember the mid for the output # Remember the mid for the output
mid_drain_offset = vector(nmos_drain_pos.x,self.output_pos.y) mid_drain_offset = vector(nmos_drain_pos.x, self.output_pos.y)
if self.route_output == True: if self.route_output:
# This extends the output to the edge of the cell # 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", self.add_layout_pin_segment_center(text="Z",
layer="metal1", layer="metal1",
start=mid_drain_offset, start=mid_drain_offset,
@ -238,8 +254,8 @@ class pinv(pgate.pgate):
# This leaves the output as an internal pin (min sized) # This leaves the output as an internal pin (min sized)
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal1", 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): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -251,9 +267,9 @@ class pinv(pgate.pgate):
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
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): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
@ -268,27 +284,35 @@ class pinv(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.5
return transition_prob*(c_load + c_para) return transition_prob * (c_load + c_para)
def input_load(self): 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 return self.nmos_size + self.pmos_size
def get_stage_effort(self, cout, inp_is_rise=True): 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 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, Optional is_rise refers to the input direction rise/fall.
self.size, Input inverted by this stage.
self.input_load(), """
cout, parasitic_delay = 1
parasitic_delay, return logical_effort.logical_effort(self.name,
self.size,
self.input_load(),
cout,
parasitic_delay,
not inp_is_rise) not inp_is_rise)
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) 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 debug
import pgate import pgate
from tech import drc
from math import log
from vector import vector from vector import vector
from globals import OPTS
from sram_factory import factory from sram_factory import factory
class pinvbuf(pgate.pgate): class pinvbuf(pgate.pgate):
""" """
This is a simple inverter/buffer used for driving loads. It is 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 # 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 # should be sized "half" to prevent loading of the first stage
self.size = size self.size = size
self.predriver_size = max(int(self.size/(self.stage_effort/2)),1) self.predriver_size = max(int(self.size / (self.stage_effort / 2)), 1)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
# Creates the netlist and layout
pgate.pgate.__init__(self, name)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -44,8 +41,8 @@ class pinvbuf(pgate.pgate):
def create_layout(self): def create_layout(self):
self.width = 2*self.inv1.width + self.inv2.width self.width = 2 * self.inv1.width + self.inv2.width
self.height = 2*self.inv1.height self.height = 2 * self.inv1.height
self.place_modules() self.place_modules()
self.route_wires() self.route_wires()
@ -53,7 +50,6 @@ class pinvbuf(pgate.pgate):
self.offset_all_coordinates() self.offset_all_coordinates()
def add_pins(self): def add_pins(self):
self.add_pin("A") self.add_pin("A")
self.add_pin("Zb") self.add_pin("Zb")
@ -64,96 +60,100 @@ class pinvbuf(pgate.pgate):
def add_modules(self): def add_modules(self):
# Shield the cap, but have at least a stage effort of 4 # Shield the cap, but have at least a stage effort of 4
input_size = max(1,int(self.predriver_size/self.stage_effort)) 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.inv = factory.create(module_type="pinv",
size=input_size,
height=self.row_height)
self.add_mod(self.inv) 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.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) self.add_mod(self.inv2)
def create_insts(self): def create_insts(self):
# Create INV1 (capacitance shield) # Create INV1 (capacitance shield)
self.inv1_inst=self.add_inst(name="buf_inv1", self.inv1_inst = self.add_inst(name="buf_inv1",
mod=self.inv) mod=self.inv)
self.connect_inst(["A", "zb_int", "vdd", "gnd"]) self.connect_inst(["A", "zb_int", "vdd", "gnd"])
self.inv2_inst = self.add_inst(name="buf_inv2",
self.inv2_inst=self.add_inst(name="buf_inv2", mod=self.inv1)
mod=self.inv1) self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.connect_inst(["zb_int", "z_int", "vdd", "gnd"])
self.inv3_inst=self.add_inst(name="buf_inv3", self.inv3_inst = self.add_inst(name="buf_inv3",
mod=self.inv2) mod=self.inv2)
self.connect_inst(["z_int", "Zb", "vdd", "gnd"]) self.connect_inst(["z_int", "Zb", "vdd", "gnd"])
self.inv4_inst=self.add_inst(name="buf_inv4", self.inv4_inst = self.add_inst(name="buf_inv4",
mod=self.inv2) mod=self.inv2)
self.connect_inst(["zb_int", "Z", "vdd", "gnd"]) self.connect_inst(["zb_int", "Z", "vdd", "gnd"])
def place_modules(self): def place_modules(self):
# Add INV1 to the left (capacitance shield) # 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 # 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 # 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 # Add INV4 flipped to the bottom aligned with INV2
self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),2*self.inv2.height), self.inv4_inst.place(offset=vector(self.inv2_inst.rx(),
mirror = "MX") 2 * self.inv2.height),
mirror="MX")
def route_wires(self): def route_wires(self):
# inv1 Z to inv2 A # inv1 Z to inv2 A
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a2_pin = self.inv2_inst.get_pin("A") a2_pin = self.inv2_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a2_pin.cy()) mid_point = vector(z1_pin.cx(), a2_pin.cy())
self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()]) self.add_path("metal1", [z1_pin.center(), mid_point, a2_pin.center()])
# inv2 Z to inv3 A # inv2 Z to inv3 A
z2_pin = self.inv2_inst.get_pin("Z") z2_pin = self.inv2_inst.get_pin("Z")
a3_pin = self.inv3_inst.get_pin("A") 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()]) self.add_path("metal1", [z2_pin.center(), mid_point, a3_pin.center()])
# inv1 Z to inv4 A (up and over) # inv1 Z to inv4 A (up and over)
z1_pin = self.inv1_inst.get_pin("Z") z1_pin = self.inv1_inst.get_pin("Z")
a4_pin = self.inv4_inst.get_pin("A") a4_pin = self.inv4_inst.get_pin("A")
mid_point = vector(z1_pin.cx(), a4_pin.cy()) 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_wire(("metal1", "via1", "metal2"),
self.add_via_center(layers=("metal1","via1","metal2"), [z1_pin.center(), mid_point, a4_pin.center()])
self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z1_pin.center()) offset=z1_pin.center())
def add_layout_pins(self): def add_layout_pins(self):
# Continous vdd rail along with label. # 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", self.add_layout_pin(text="vdd",
layer="metal1", layer="metal1",
offset=vdd_pin.ll().scale(0,1), offset=vdd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
# Continous vdd rail along with label. # 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", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=gnd_pin.height()) height=gnd_pin.height())
# Continous gnd rail along with label. # 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", self.add_layout_pin(text="gnd",
layer="metal1", layer="metal1",
offset=gnd_pin.ll().scale(0,1), offset=gnd_pin.ll().scale(0, 1),
width=self.width, width=self.width,
height=vdd_pin.height()) height=vdd_pin.height())
@ -161,22 +161,21 @@ class pinvbuf(pgate.pgate):
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
layer="metal2", layer="metal2",
offset=z_pin.center()) offset=z_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=z_pin.center()) offset=z_pin.center())
zb_pin = self.inv3_inst.get_pin("Z") zb_pin = self.inv3_inst.get_pin("Z")
self.add_layout_pin_rect_center(text="Zb", self.add_layout_pin_rect_center(text="Zb",
layer="metal2", layer="metal2",
offset=zb_pin.center()) offset=zb_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=zb_pin.center()) offset=zb_pin.center())
a_pin = self.inv1_inst.get_pin("A") a_pin = self.inv1_inst.get_pin("A")
self.add_layout_pin_rect_center(text="A", self.add_layout_pin_rect_center(text="A",
layer="metal2", layer="metal2",
offset=a_pin.center()) offset=a_pin.center())
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=a_pin.center()) offset=a_pin.center())
def determine_clk_buf_stage_efforts(self, external_cout, inp_is_rise=False): 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): def determine_clk_buf_bar_stage_efforts(self, external_cout, inp_is_rise=False):
"""Get the stage efforts of the clk -> clk_buf path""" """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 = [] stage_effort_list = []
stage1_cout = self.inv1.get_cin() + self.inv2.get_cin() stage1_cout = self.inv1.get_cin() + self.inv2.get_cin()
stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise) stage1 = self.inv.get_stage_effort(stage1_cout, inp_is_rise)

View File

@ -10,10 +10,10 @@ import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pnand2(pgate.pgate): class pnand2(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nand. 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): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nand """ """ 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.add_comment("size: {}".format(size))
self.size = size self.size = size
self.nmos_size = 2*size self.nmos_size = 2 * size
self.pmos_size = parameter["beta"]*size self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # 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 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
@ -61,7 +63,6 @@ class pnand2(pgate.pgate):
dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"] dir_list = ["INPUT", "INPUT", "OUTPUT", "POWER", "GROUND"]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
def add_ptx(self): def add_ptx(self):
""" Create the PMOS and NMOS transistors. """ """ Create the PMOS and NMOS transistors. """
self.nmos = factory.create(module_type="ptx", self.nmos = factory.create(module_type="ptx",
@ -90,113 +91,126 @@ class pnand2(pgate.pgate):
self.m3_space + contact.m2m3.second_layer_width) 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 # source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + contact.active.width \ self.well_width = 2 * self.pmos.active_width + contact.active.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") + 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width 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.
# This is the extra space needed to ensure DRC rules to the active contacts # This is the extra space needed to ensure DRC rules
extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # 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 # 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) drc("poly_extend_active"), self.poly_space)
def route_supply_rails(self): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5*self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Add PMOS and NMOS to the netlist. Add PMOS and NMOS to the netlist.
""" """
self.pmos1_inst=self.add_inst(name="pnand2_pmos1", self.pmos1_inst = self.add_inst(name="pnand2_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand2_pmos2", self.pmos2_inst = self.add_inst(name="pnand2_pmos2",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "B", "vdd", "vdd"]) self.connect_inst(["Z", "B", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand2_nmos1", self.nmos1_inst = self.add_inst(name="pnand2_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "B", "net1", "gnd"]) self.connect_inst(["Z", "B", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand2_nmos2", self.nmos2_inst = self.add_inst(name="pnand2_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net1", "A", "gnd", "gnd"]) self.connect_inst(["net1", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, 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.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset self.pmos2_pos = pmos1_pos + self.overlap_offset
self.pmos2_inst.place(self.pmos2_pos) self.pmos2_inst.place(self.pmos2_pos)
nmos1_pos = vector(self.pmos.active_offset.x,
nmos1_pos = vector(self.pmos.active_offset.x, self.top_bottom_space) self.top_bottom_space)
self.nmos1_inst.place(nmos1_pos) self.nmos1_inst.place(nmos1_pos)
self.nmos2_pos = nmos1_pos + self.overlap_offset self.nmos2_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # 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)) self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self): 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_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
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): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height + self.m2_space + 0.5*self.m2_width inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + 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 # This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos_pin = self.pmos1_inst.get_pin("D") pmos_pin = self.pmos1_inst.get_pin("D")
top_pin_offset = pmos_pin.center() top_pin_offset = pmos_pin.center()
# NMOS2 drain # NMOS2 drain
@ -204,24 +218,26 @@ class pnand2(pgate.pgate):
bottom_pin_offset = nmos_pin.center() bottom_pin_offset = nmos_pin.center()
# Output pin # Output pin
out_offset = vector(nmos_pin.center().x + self.m1_pitch,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 # Midpoints of the L routes go horizontal first then vertical
mid1_offset = vector(out_offset.x, top_pin_offset.y) 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"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center(), offset=pmos_pin.center(),
directions=("V","H")) directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center(), offset=nmos_pin.center(),
directions=("V","H")) directions=("V", "H"))
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=out_offset) offset=out_offset)
# PMOS1 to mid-drain to NMOS2 drain # 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 # This extends the output to the edge of the cell
self.add_layout_pin_rect_center(text="Z", self.add_layout_pin_rect_center(text="Z",
@ -243,21 +259,32 @@ class pnand2(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.1875
return transition_prob*(c_load + c_para) return transition_prob * (c_load + c_para)
def input_load(self): def input_load(self):
"""Return the relative input capacitance of a single input""" """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): 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 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) 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): 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) 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 import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort import logical_effort
from sram_factory import factory from sram_factory import factory
class pnand3(pgate.pgate): class pnand3(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nand. This module generates gds of a parametrically sized 2-input nand.
@ -22,24 +22,25 @@ class pnand3(pgate.pgate):
def __init__(self, name, size=1, height=None): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 3 input nand """ """ 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)) self.add_comment("size: {}".format(size))
# We have trouble pitch matching a 3x sizes to the bitcell... # We have trouble pitch matching a 3x sizes to the bitcell...
# If we relax this, we could size this better. # If we relax this, we could size this better.
self.size = size self.size = size
self.nmos_size = 2*size self.nmos_size = 2 * size
self.pmos_size = parameter["beta"]*size self.pmos_size = parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # 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 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def add_pins(self): def add_pins(self):
""" Adds pins for spice netlist """ """ Adds pins for spice netlist """
@ -90,41 +91,44 @@ class pnand3(pgate.pgate):
# Two PMOS devices and a well contact. Separation between each. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 3*self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = 3 * self.pmos.active_width + self.pmos.active_contact.width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") \ + 2 * drc("active_to_body_active") + 2 * drc("well_enclosure_active") \
- self.overlap_offset.x - self.overlap_offset.x
self.width = self.well_width 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.
# This will help with the wells and the input/output placement # 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") 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 # 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 \
drc("poly_extend_active"), self.poly_space) + extra_contact_space,
drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS in the netlist. Create the PMOS and NMOS in the netlist.
""" """
self.pmos1_inst=self.add_inst(name="pnand3_pmos1", self.pmos1_inst = self.add_inst(name="pnand3_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "Z", "vdd"]) self.connect_inst(["vdd", "A", "Z", "vdd"])
self.pmos2_inst = self.add_inst(name="pnand3_pmos2", self.pmos2_inst = self.add_inst(name="pnand3_pmos2",
@ -135,27 +139,27 @@ class pnand3(pgate.pgate):
mod=self.pmos) mod=self.pmos)
self.connect_inst(["Z", "C", "vdd", "vdd"]) self.connect_inst(["Z", "C", "vdd", "vdd"])
self.nmos1_inst=self.add_inst(name="pnand3_nmos1", self.nmos1_inst = self.add_inst(name="pnand3_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "C", "net1", "gnd"]) self.connect_inst(["Z", "C", "net1", "gnd"])
self.nmos2_inst=self.add_inst(name="pnand3_nmos2", self.nmos2_inst = self.add_inst(name="pnand3_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net1", "B", "net2", "gnd"]) self.connect_inst(["net1", "B", "net2", "gnd"])
self.nmos3_inst=self.add_inst(name="pnand3_nmos3", self.nmos3_inst = self.add_inst(name="pnand3_nmos3",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["net2", "A", "gnd", "gnd"]) self.connect_inst(["net2", "A", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place the PMOS and NMOS in the layout at the upper-most and lowest position Place the PMOS and NMOS in the layout at the upper-most
to provide maximum routing in channel and lowest position to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, pmos1_pos = vector(self.pmos.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.pmos1_inst.place(pmos1_pos)
pmos2_pos = pmos1_pos + self.overlap_offset pmos2_pos = pmos1_pos + self.overlap_offset
@ -165,7 +169,8 @@ class pnand3(pgate.pgate):
self.pmos3_inst.place(self.pmos3_pos) 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) self.nmos1_inst.place(nmos1_pos)
nmos2_pos = nmos1_pos + self.overlap_offset nmos2_pos = nmos1_pos + self.overlap_offset
@ -175,7 +180,7 @@ class pnand3(pgate.pgate):
self.nmos3_inst.place(self.nmos3_pos) self.nmos3_inst.place(self.nmos3_pos)
# This should be placed at the top of the NMOS well # 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): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
@ -183,42 +188,53 @@ class pnand3(pgate.pgate):
self.add_nwell_contact(self.pmos, self.pmos3_pos) self.add_nwell_contact(self.pmos, self.pmos3_pos)
self.add_pwell_contact(self.nmos, self.nmos3_pos) self.add_pwell_contact(self.nmos, self.nmos3_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
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): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
# wire space or wire and one contact space # wire space or wire and one contact space
metal_spacing = max(self.m1_space + self.m1_width, self.m2_space + self.m2_width, metal_spacing = max(self.m1_space + self.m1_width,
self.m1_space + 0.5*contact.poly.width + 0.5*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 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 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.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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS1 drain # PMOS1 drain
pmos1_pin = self.pmos1_inst.get_pin("D") pmos1_pin = self.pmos1_inst.get_pin("D")
# PMOS3 drain # PMOS3 drain
pmos3_pin = self.pmos3_inst.get_pin("D") pmos3_pin = self.pmos3_inst.get_pin("D")
# NMOS3 drain # NMOS3 drain
nmos3_pin = self.nmos3_inst.get_pin("D") nmos3_pin = self.nmos3_inst.get_pin("D")
# Go up to metal2 for ease on all output pins # Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -229,10 +245,10 @@ class pnand3(pgate.pgate):
offset=nmos3_pin.center()) offset=nmos3_pin.center())
# PMOS3 and NMOS3 are drain aligned # 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) # Route in the A input track (top track)
mid_offset = vector(nmos3_pin.center().x,self.inputA_yoffset) mid_offset = vector(nmos3_pin.center().x, self.inputA_yoffset)
self.add_path("metal2",[pmos1_pin.bc(), mid_offset, nmos3_pin.uc()]) self.add_path("metal2", [pmos1_pin.bc(), mid_offset, nmos3_pin.uc()])
# This extends the output to the edge of the cell # This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
@ -256,21 +272,32 @@ class pnand3(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 transition_prob = 0.1094
return transition_prob*(c_load + c_para) return transition_prob *(c_load + c_para)
def input_load(self): def input_load(self):
"""Return the relative input capacitance of a single input""" """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): 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 Returns an object representing the parameters for delay in tau units.
return logical_effort.logical_effort(self.name, self.size, self.input_load(), cout, parasitic_delay, not inp_is_rise) 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): 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) 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 import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector from vector import vector
from globals import OPTS
import logical_effort
from sram_factory import factory from sram_factory import factory
class pnor2(pgate.pgate): class pnor2(pgate.pgate):
""" """
This module generates gds of a parametrically sized 2-input nor. 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): def __init__(self, name, size=1, height=None):
""" Creates a cell for a simple 2 input nor """ """ 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.add_comment("size: {}".format(size))
self.nmos_size = size self.nmos_size = size
# We will just make this 1.5 times for now. NORs are not ideal anyhow. # We will just make this 1.5 times for now. NORs are not ideal anyhow.
self.pmos_size = 1.5*parameter["beta"]*size self.pmos_size = 1.5 * parameter["beta"] * size
self.nmos_width = self.nmos_size*drc("minwidth_tx") self.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_size*drc("minwidth_tx") self.pmos_width = self.pmos_size * drc("minwidth_tx")
# FIXME: Allow these to be sized # 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 self.tx_mults = 1
# Creates the netlist and layout # Creates the netlist and layout
pgate.pgate.__init__(self, name, height) pgate.pgate.__init__(self, name, height)
def create_netlist(self): def create_netlist(self):
self.add_pins() self.add_pins()
@ -89,44 +89,48 @@ class pnor2(pgate.pgate):
self.m2_space + contact.m2m3.first_layer_width, self.m2_space + contact.m2m3.first_layer_width,
self.m3_space + contact.m2m3.second_layer_width) 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
# source and drain pins # offset to overlap the source and drain pins
self.overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # Enclosure space on the sides.
self.well_width = 2*self.pmos.active_width + self.pmos.active_contact.width \ self.well_width = 2 * self.pmos.active_width \
+ 2*drc("active_to_body_active") + 2*drc("well_enclosure_active") + self.pmos.active_contact.width \
+ 2 * drc("active_to_body_active") \
+ 2 * drc("well_enclosure_active")
self.width = self.well_width 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.
# This is the extra space needed to ensure DRC rules to the active contacts # This is the extra space needed to ensure DRC rules
extra_contact_space = max(-self.nmos.get_pin("D").by(),0) # 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 # 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) drc("poly_extend_active"),
self.poly_space)
def route_supply_rails(self): def route_supply_rails(self):
""" Add vdd/gnd rails to the top and bottom. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Add PMOS and NMOS to the layout at the upper-most and lowest position Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
self.pmos1_inst=self.add_inst(name="pnor2_pmos1", self.pmos1_inst = self.add_inst(name="pnor2_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["vdd", "A", "net1", "vdd"]) self.connect_inst(["vdd", "A", "net1", "vdd"])
self.pmos2_inst = self.add_inst(name="pnor2_pmos2", self.pmos2_inst = self.add_inst(name="pnor2_pmos2",
@ -134,23 +138,23 @@ class pnor2(pgate.pgate):
self.connect_inst(["net1", "B", "Z", "vdd"]) self.connect_inst(["net1", "B", "Z", "vdd"])
self.nmos1_inst=self.add_inst(name="pnor2_nmos1", self.nmos1_inst = self.add_inst(name="pnor2_nmos1",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "A", "gnd", "gnd"]) self.connect_inst(["Z", "A", "gnd", "gnd"])
self.nmos2_inst=self.add_inst(name="pnor2_nmos2", self.nmos2_inst = self.add_inst(name="pnor2_nmos2",
mod=self.nmos) mod=self.nmos)
self.connect_inst(["Z", "B", "gnd", "gnd"]) self.connect_inst(["Z", "B", "gnd", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Add PMOS and NMOS to the layout at the upper-most and lowest position Add PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos1_pos = vector(self.pmos.active_offset.x, pmos1_pos = vector(self.pmos.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.pmos1_inst.place(pmos1_pos)
self.pmos2_pos = pmos1_pos + self.overlap_offset 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_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # 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)) self.output_pos = vector(0,
0.5 * (pmos1_pos.y + nmos1_pos.y + self.nmos.active_height))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def add_well_contacts(self): def add_well_contacts(self):
""" Add n/p well taps to the layout and connect to supplies """ """ Add n/p well taps to the layout and connect to supplies """
self.add_nwell_contact(self.pmos, self.pmos2_pos) self.add_nwell_contact(self.pmos, self.pmos2_pos)
self.add_pwell_contact(self.nmos, self.nmos2_pos) self.add_pwell_contact(self.nmos, self.nmos2_pos)
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
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.nmos2_inst, "D", "gnd")
self.connect_pin_to_rail(self.pmos1_inst,"S","vdd")
self.connect_pin_to_rail(self.pmos1_inst, "S", "vdd")
def route_inputs(self): def route_inputs(self):
""" Route the A and B inputs """ """ Route the A and B inputs """
# Use M2 spaces so we can drop vias on the pins later! # 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 inputB_yoffset = self.nmos2_pos.y + self.nmos.active_height \
self.route_input_gate(self.pmos2_inst, self.nmos2_inst, inputB_yoffset, "B", position="center") + 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 # This will help with the wells and the input/output placement
self.inputA_yoffset = inputB_yoffset + self.input_spacing 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): def route_output(self):
""" Route the Z output """ """ Route the Z output """
# PMOS2 drain # PMOS2 drain
pmos_pin = self.pmos2_inst.get_pin("D") pmos_pin = self.pmos2_inst.get_pin("D")
# NMOS1 drain # NMOS1 drain
nmos_pin = self.nmos1_inst.get_pin("D") 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 # Go up to metal2 for ease on all output pins
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=pmos_pin.center()) offset=pmos_pin.center())
m1m2_contact=self.add_via_center(layers=("metal1", "via1", "metal2"), m1m2_contact = self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_pin.center()) offset=nmos_pin.center())
mid1_offset = vector(pmos_pin.center().x,nmos2_pin.center().y) mid1_offset = vector(pmos_pin.center().x, nmos2_pin.center().y)
mid2_offset = vector(pmos_pin.center().x,self.inputA_yoffset) mid2_offset = vector(pmos_pin.center().x, self.inputA_yoffset)
mid3_offset = mid2_offset + vector(self.m2_width,0) mid3_offset = mid2_offset + vector(self.m2_width, 0)
# PMOS1 to mid-drain to NMOS2 drain # PMOS1 to mid-drain to NMOS2 drain
self.add_path("metal2",[pmos_pin.bc(), mid2_offset, mid3_offset]) self.add_path("metal2",
self.add_path("metal2",[nmos_pin.rc(), mid1_offset, mid2_offset]) [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 # This extends the output to the edge of the cell
self.add_via_center(layers=("metal1", "via1", "metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=mid3_offset) offset=mid3_offset)
@ -240,10 +252,11 @@ class pnor2(pgate.pgate):
def calculate_effective_capacitance(self, load): def calculate_effective_capacitance(self, load):
"""Computes effective capacitance. Results in fF""" """Computes effective capacitance. Results in fF"""
c_load = load 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 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.""" """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 globals import OPTS
from sram_factory import factory from sram_factory import factory
class precharge(design.design): class precharge(design.design):
""" """
Creates a single precharge cell Creates a single precharge cell
@ -25,16 +26,15 @@ class precharge(design.design):
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
self.beta = parameter["beta"] 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.width = self.bitcell.width
self.bitcell_bl = bitcell_bl self.bitcell_bl = bitcell_bl
self.bitcell_br = bitcell_br self.bitcell_br = bitcell_br
# Creates the netlist and layout # Creates the netlist and layout
# Since it has variable height, it is not a pgate. # Since it has variable height, it is not a pgate.
self.create_netlist() self.create_netlist()
if not OPTS.netlist_only: if not OPTS.netlist_only:
self.create_layout() self.create_layout()
self.DRC_LVS() self.DRC_LVS()
@ -53,7 +53,8 @@ class precharge(design.design):
self.connect_to_bitlines() self.connect_to_bitlines()
def add_pins(self): 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): def add_ptx(self):
""" """
@ -64,16 +65,13 @@ class precharge(design.design):
tx_type="pmos") tx_type="pmos")
self.add_mod(self.pmos) self.add_mod(self.pmos)
def route_vdd_rail(self): def route_vdd_rail(self):
""" """
Adds a vdd rail at the top of the cell Adds a vdd rail at the top of the cell
""" """
# Adds the rail across the width 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", self.add_rect_center(layer="metal1",
offset=vdd_position, offset=vdd_position,
width=self.width, width=self.width,
@ -87,44 +85,43 @@ class precharge(design.design):
# Add vdd pin above the transistor # Add vdd pin above the transistor
self.add_power_pin("vdd", pmos_pin.center(), vertical=True) self.add_power_pin("vdd", pmos_pin.center(), vertical=True)
def create_ptx(self): def create_ptx(self):
""" """
Create both the upper_pmos and lower_pmos to the module Create both the upper_pmos and lower_pmos to the module
""" """
self.lower_pmos_inst=self.add_inst(name="lower_pmos", self.lower_pmos_inst = self.add_inst(name="lower_pmos",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en_bar", "br", "vdd"]) self.connect_inst(["bl", "en_bar", "br", "vdd"])
self.upper_pmos1_inst=self.add_inst(name="upper_pmos1", self.upper_pmos1_inst = self.add_inst(name="upper_pmos1",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["bl", "en_bar", "vdd", "vdd"]) self.connect_inst(["bl", "en_bar", "vdd", "vdd"])
self.upper_pmos2_inst=self.add_inst(name="upper_pmos2", self.upper_pmos2_inst = self.add_inst(name="upper_pmos2",
mod=self.pmos) mod=self.pmos)
self.connect_inst(["br", "en_bar", "vdd", "vdd"]) self.connect_inst(["br", "en_bar", "vdd", "vdd"])
def place_ptx(self): def place_ptx(self):
""" """
Place both the upper_pmos and lower_pmos to the module Place both the upper_pmos and lower_pmos to the module
""" """
# Compute the other pmos2 location, but determining offset to overlap the # Compute the other pmos2 location,
# source and drain pins # but determining offset to overlap the source and drain pins
overlap_offset = self.pmos.get_pin("D").ll() - self.pmos.get_pin("S").ll() 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 # This is how much the contact is placed inside the ptx active
contact_xdiff = self.pmos.get_pin("S").lx() contact_xdiff = self.pmos.get_pin("S").lx()
# adds the lower pmos to layout # adds the lower pmos to layout
bl_xoffset = self.bitcell.get_pin(self.bitcell_bl).lx() 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.pmos.active_offset.y)
self.lower_pmos_inst.place(self.lower_pmos_position) self.lower_pmos_inst.place(self.lower_pmos_position)
# adds the upper pmos(s) to layout # 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_pos = self.lower_pmos_position + vector(0, ydiff)
self.upper_pmos1_inst.place(self.upper_pmos1_pos) self.upper_pmos1_inst.place(self.upper_pmos1_pos)
@ -146,7 +143,9 @@ class precharge(design.design):
# connects the two poly for the two upper pmos(s) # connects the two poly for the two upper pmos(s)
offset = offset + vector(0, ylength - self.poly_width) 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", self.add_rect(layer="poly",
offset=offset, offset=offset,
width=xlength, width=xlength,
@ -158,16 +157,16 @@ class precharge(design.design):
""" """
# adds the en contact to connect the gates to the en rail on metal1 # 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"), self.add_via_center(layers=("poly", "contact", "metal1"),
offset=offset) offset=offset)
# adds the en rail on metal1 # adds the en rail on metal1
self.add_layout_pin_segment_center(text="en_bar", self.add_layout_pin_segment_center(text="en_bar",
layer="metal1", layer="metal1",
start=offset.scale(0,1), start=offset.scale(0, 1),
end=offset.scale(0,1)+vector(self.width,0)) end=offset.scale(0, 1) + vector(self.width, 0))
def place_nwell_and_contact(self): def place_nwell_and_contact(self):
""" """
@ -175,8 +174,9 @@ class precharge(design.design):
""" """
# adds the contact from active to metal1 # adds the contact from active to metal1
well_contact_pos = self.upper_pmos1_inst.get_pin("D").center().scale(1,0) \ 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")) + vector(0, self.upper_pmos1_inst.uy() + contact.well.height / 2 \
+ drc("well_extend_active"))
self.add_via_center(layers=("active", "contact", "metal1"), self.add_via_center(layers=("active", "contact", "metal1"),
offset=well_contact_pos, offset=well_contact_pos,
implant_type="n", implant_type="n",
@ -187,18 +187,18 @@ class precharge(design.design):
# nwell should span the whole design since it is pmos only # nwell should span the whole design since it is pmos only
self.add_rect(layer="nwell", self.add_rect(layer="nwell",
offset=vector(0,0), offset=vector(0, 0),
width=self.width, width=self.width,
height=self.height) height=self.height)
def route_bitlines(self): def route_bitlines(self):
""" """
Adds both bit-line and bit-line-bar to the module Adds both bit-line and bit-line-bar to the module
""" """
# adds the BL on metal 2 # 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", self.bl_pin = self.add_layout_pin(text="bl",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
@ -206,7 +206,8 @@ class precharge(design.design):
height=self.height) height=self.height)
# adds the BR on metal 2 # 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", self.br_pin = self.add_layout_pin(text="br",
layer="metal2", layer="metal2",
offset=offset, offset=offset,
@ -218,60 +219,64 @@ class precharge(design.design):
Connect the bitlines to the devices Connect the bitlines to the devices
""" """
self.add_bitline_contacts() self.add_bitline_contacts()
self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),self.get_pin("bl")) self.connect_pmos_m2(self.lower_pmos_inst.get_pin("S"),
self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),self.get_pin("bl")) self.get_pin("bl"))
self.connect_pmos_m1(self.lower_pmos_inst.get_pin("D"),self.get_pin("br")) self.connect_pmos_m2(self.upper_pmos1_inst.get_pin("S"),
self.connect_pmos_m1(self.upper_pmos2_inst.get_pin("D"),self.get_pin("br")) 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): def add_bitline_contacts(self):
""" """
Adds contacts/via from metal1 to metal2 for bit-lines 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") upper_pin = self.upper_pmos1_inst.get_pin("S")
lower_pin = self.lower_pmos_inst.get_pin("S") lower_pin = self.lower_pmos_inst.get_pin("S")
# BL goes up to M2 at the transistor # BL goes up to M2 at the transistor
self.bl_contact=self.add_via_center(layers=stack, self.bl_contact =self.add_via_center(layers=stack,
offset=upper_pin.center(), offset=upper_pin.center(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset=lower_pin.center(), offset=lower_pin.center(),
directions=("V","V")) directions=("V", "V"))
# BR routes over on M1 first # BR routes over on M1 first
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), upper_pin.cy()), offset=vector(self.br_pin.cx(), upper_pin.cy()),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=stack, self.add_via_center(layers=stack,
offset = vector(self.br_pin.cx(), lower_pin.cy()), offset=vector(self.br_pin.cx(), lower_pin.cy()),
directions=("V","V")) directions=("V", "V"))
def connect_pmos_m1(self, pmos_pin, bit_pin): 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()) 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()) 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): 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()) 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()) 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): def get_en_cin(self):
"""Get the relative capacitance of the enable in the precharge cell""" """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. # The enable connect to three pmos gates
# They all use the same size pmos.
pmos_cin = self.pmos.get_cin() pmos_cin = self.pmos.get_cin()
return 3*pmos_cin return 3 * pmos_cin

View File

@ -10,16 +10,12 @@ import pgate
import debug import debug
from tech import drc, parameter, spice from tech import drc, parameter, spice
from vector import vector 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 from sram_factory import factory
class ptristate_inv(pgate.pgate): 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 There is some flexibility in the size, but we do not allow multiple fingers
to fit in the cell height. to fit in the cell height.
@ -27,14 +23,16 @@ class ptristate_inv(pgate.pgate):
def __init__(self, name, size=1, height=None): 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)) self.add_comment("size: {}".format(size))
# We are 2x since there are two series devices # We are 2x since there are two series devices
self.size = 2*size self.size = 2 * size
self.nmos_size = size self.nmos_size = size
self.beta = parameter["beta"] 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.nmos_width = self.nmos_size * drc("minwidth_tx")
self.pmos_width = self.pmos_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. # Two PMOS devices and a well contact. Separation between each.
# Enclosure space on the sides. # 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 # 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. # Height is an input parameter, so it is not recomputed.
# Make sure we can put a well above and below # 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. """ """ Add vdd/gnd rails to the top and bottom. """
self.add_layout_pin_rect_center(text="gnd", self.add_layout_pin_rect_center(text="gnd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,0), offset=vector(0.5 * self.width, 0),
width=self.width) width=self.width)
self.add_layout_pin_rect_center(text="vdd", self.add_layout_pin_rect_center(text="vdd",
layer="metal1", layer="metal1",
offset=vector(0.5*self.width,self.height), offset=vector(0.5 * self.width, self.height),
width=self.width) width=self.width)
def create_ptx(self): def create_ptx(self):
""" """
Create the PMOS and NMOS netlist. Create the PMOS and NMOS netlist.
""" """
# These are the inverter PMOS/NMOS # 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.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"]) self.connect_inst(["gnd", "in", "n2", "gnd"])
# These are the tristate PMOS/NMOS # These are the tristate PMOS/NMOS
self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos) self.pmos2_inst = self.add_inst(name="ptri_pmos2", mod=self.pmos)
self.connect_inst(["out", "en_bar", "n1", "vdd"]) 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"]) self.connect_inst(["out", "en", "n2", "gnd"])
def place_ptx(self): def place_ptx(self):
""" """
Place PMOS and NMOS to the layout at the upper-most and lowest position Place PMOS and NMOS to the layout at the upper-most and lowest position
to provide maximum routing in channel to provide maximum routing in channel
""" """
pmos_yoff = self.height - self.pmos.active_height - self.top_bottom_space - 0.5*contact.well.height pmos_yoff = self.height - self.pmos.active_height \
nmos_yoff = self.top_bottom_space + 0.5*contact.well.height - self.top_bottom_space - 0.5 * contact.well.height
nmos_yoff = self.top_bottom_space + 0.5 * contact.well.height
# Tristate transistors # Tristate transistors
pmos1_pos = vector(self.pmos.active_offset.x, pmos_yoff) 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_pos = nmos1_pos + self.overlap_offset
self.nmos2_inst.place(self.nmos2_pos) self.nmos2_inst.place(self.nmos2_pos)
# Output position will be in between the PMOS and NMOS # Output position will be in between the PMOS and NMOS
self.output_pos = vector(0, 0.5*(pmos_yoff + nmos_yoff + self.nmos.height)) self.output_pos = vector(0,
0.5 * (pmos_yoff + nmos_yoff + self.nmos.height))
# This will help with the wells # This will help with the wells
self.well_pos = vector(0,self.nmos1_inst.uy()) self.well_pos = vector(0, self.nmos1_inst.uy())
def route_inputs(self): def route_inputs(self):
""" Route the gates """ """ 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.pmos2_inst, "en_bar", position="left")
self.route_single_gate(self.nmos2_inst, "en", position="left") self.route_single_gate(self.nmos2_inst, "en", position="left")
def route_outputs(self): def route_outputs(self):
""" Route the output (drains) together. """ """ Route the output (drains) together. """
@ -181,40 +183,41 @@ class ptristate_inv(pgate.pgate):
self.add_layout_pin(text="out", self.add_layout_pin(text="out",
layer="metal1", layer="metal1",
offset=nmos_drain_pos, 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): 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") layer_stack = ("active", "contact", "metal1")
drain_pos = self.nmos1_inst.get_pin("S").center() drain_pos = self.nmos1_inst.get_pin("S").center()
vdd_pos = self.get_pin("vdd").center() vdd_pos = self.get_pin("vdd").center()
self.nwell_contact=self.add_via_center(layers=layer_stack, self.nwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,vdd_pos.y), offset=vector(drain_pos.x, vdd_pos.y),
implant_type="n", implant_type="n",
well_type="n") well_type="n")
gnd_pos = self.get_pin("gnd").center() gnd_pos = self.get_pin("gnd").center()
self.pwell_contact=self.add_via_center(layers=layer_stack, self.pwell_contact = self.add_via_center(layers=layer_stack,
offset=vector(drain_pos.x,gnd_pos.y), offset=vector(drain_pos.x, gnd_pos.y),
implant_type="p", implant_type="p",
well_type="p") well_type="p")
def connect_rails(self): def connect_rails(self):
""" Connect the nmos and pmos to its respective power rails """ """ Connect the nmos and pmos to its respective power rails """
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")
def analytical_power(self, corner, load): def analytical_power(self, corner, load):
"""Returns dynamic and leakage power. Results in nW""" """Returns dynamic and leakage power. Results in nW"""
#Power in this module currently not defined. Returns 0 nW (leakage and dynamic). # Power in this module currently not defined.
total_power = self.return_power() # Returns 0 nW (leakage and dynamic).
total_power = self.return_power()
return total_power return total_power
def get_cin(self): 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 import debug
from tech import drc, spice from tech import drc, spice
from vector import vector from vector import vector
from globals import OPTS
from sram_factory import factory from sram_factory import factory
class ptx(design.design): class ptx(design.design):
""" """
This module generates gds and spice of a parametrically NMOS or 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. 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 # We need to keep unique names because outputting to GDSII
# will use the last record with a given name. I.e., you will # will use the last record with a given name. I.e., you will
# over-write a design in GDS if one has and the other doesn't # over-write a design in GDS if one has and the other doesn't
@ -34,7 +41,7 @@ class ptx(design.design):
if num_contacts: if num_contacts:
name += "_c{}".format(num_contacts) name += "_c{}".format(num_contacts)
# replace periods with underscore for newer spice compatibility # replace periods with underscore for newer spice compatibility
name=name.replace('.','_') name = name.replace('.', '_')
debug.info(3, "creating ptx {0}".format(name)) debug.info(3, "creating ptx {0}".format(name))
design.design.__init__(self, name) design.design.__init__(self, name)
@ -51,42 +58,43 @@ class ptx(design.design):
# We must always create ptx layout for pbitcell # We must always create ptx layout for pbitcell
# some transistor sizes in other netlist depend on pbitcell # some transistor sizes in other netlist depend on pbitcell
self.create_layout() self.create_layout()
def create_layout(self): def create_layout(self):
"""Calls all functions related to the generation of the layout""" """Calls all functions related to the generation of the layout"""
self.setup_layout_constants() self.setup_layout_constants()
self.add_active() self.add_active()
self.add_well_implant() self.add_well_implant()
self.add_poly() self.add_poly()
self.add_active_contacts() self.add_active_contacts()
self.translate_all(self.active_offset) self.translate_all(self.active_offset)
# for run-time, we won't check every transitor DRC independently # for run-time, we won't check every transitor DRC independently
# but this may be uncommented for debug purposes # but this may be uncommented for debug purposes
#self.DRC() # self.DRC()
def create_netlist(self): def create_netlist(self):
pin_list = ["D", "G", "S", "B"] pin_list = ["D", "G", "S", "B"]
if self.tx_type=="nmos": if self.tx_type == "nmos":
body_dir = 'GROUND' 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' body_dir = 'POWER'
dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir] dir_list = ['INOUT', 'INPUT', 'INOUT', body_dir]
self.add_pin_list(pin_list, dir_list) self.add_pin_list(pin_list, dir_list)
# self.spice.append("\n.SUBCKT {0} {1}".format(self.name, # self.spice.append("\n.SUBCKT {0} {1}".format(self.name,
# " ".join(self.pins))) # " ".join(self.pins)))
# Just make a guess since these will actually be decided in the layout later. # Just make a guess since these will actually
area_sd = 2.5*drc("minwidth_poly")*self.tx_width # be decided in the layout later.
perimeter_sd = 2*drc("minwidth_poly") + 2*self.tx_width area_sd = 2.5 * drc("minwidth_poly") * 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], perimeter_sd = 2 * drc("minwidth_poly") + 2 * self.tx_width
self.mults, main_str = "M{{0}} {{1}} {0} m={1} w={2}u l={3}u ".format(spice[self.tx_type],
self.tx_width, self.mults,
drc("minwidth_poly"), self.tx_width,
perimeter_sd, drc("minwidth_poly"))
area_sd) 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("\n* ptx " + self.spice_device)
# self.spice.append(".ENDS {0}".format(self.name)) # 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) # 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) self.poly_space)
# The contacted poly pitch (or uncontacted in an odd technology) # 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. # The enclosure of an active contact. Not sure about second term.
active_enclose_contact = max(drc("active_enclosure_contact"), 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 # 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 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, # Active width is determined by enclosure on both ends and contacted pitch,
# at least one poly and n-1 poly pitches # 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 # Active height is just the transistor width
self.active_height = self.tx_width self.active_height = self.tx_width
# Poly height must include poly extension over active # 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 # 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 # Well enclosure of active, ensure minwidth as well
if drc("has_{}well".format(self.well_type)): if drc("has_{}well".format(self.well_type)):
self.cell_well_width = max(self.active_width + 2*self.well_enclose_active, self.cell_well_width = max(self.active_width + 2 * self.well_enclose_active,
self.well_width) self.well_width)
self.cell_well_height = max(self.tx_width + 2*self.well_enclose_active, self.cell_well_height = max(self.tx_width + 2 * self.well_enclose_active,
self.well_width) self.well_width)
# We are going to shift the 0,0, so include that in the width and height # 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.height = self.cell_well_height - self.active_offset.y
self.width = self.cell_well_width - self.active_offset.x self.width = self.cell_well_width - self.active_offset.x
@ -157,17 +166,20 @@ class ptx(design.design):
self.width = self.active_width self.width = self.active_width
# The active offset is due to the well extension # 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) # 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, self.contact_offset = self.active_offset + vector(active_enclose_contact + 0.5 * self.contact_width,
0.5*self.active_height) 0.5 * self.active_height)
# Min area results are just flagged for now. # Min area results are just flagged for now.
debug.check(self.active_width*self.active_height>=drc("minarea_active"),"Minimum active area violated.") debug.check(self.active_width * self.active_height >= drc("minarea_active"),
# We do not want to increase the poly dimensions to fix an area problem as it would cause an LVS issue. "Minimum active area violated.")
debug.check(self.poly_width*self.poly_height>=drc("minarea_poly"),"Minimum poly 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): 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 # 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 poly_width = poly_positions[-1].x - poly_positions[0].x + self.poly_width
if self.tx_type == "pmos": if self.tx_type == "pmos":
# This can be limited by poly to active spacing or the poly extension # This can be limited by poly to active spacing
distance_below_active = self.poly_width + max(self.poly_to_active,0.5*self.poly_height) # or the poly extension
poly_offset = poly_positions[0] - vector(0.5*self.poly_width, distance_below_active) 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: else:
# This can be limited by poly to active spacing or the poly extension # This can be limited by poly to active spacing
distance_above_active = max(self.poly_to_active,0.5*self.poly_height) # or the poly extension
poly_offset = poly_positions[0] + vector(-0.5*self.poly_width, distance_above_active) 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 # Remove the old pin and add the new one
self.remove_layout_pin("G") # only keep the main pin self.remove_layout_pin("G") # only keep the main pin
self.add_layout_pin(text="G", 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 # This is the distance that we must route up or down from the center
# of the contacts to avoid DRC violations to the other contacts # of the contacts to avoid DRC violations to the other contacts
pin_offset = vector(0, 0.5*self.active_contact.second_layer_height \ pin_offset = vector(0, 0.5 * self.active_contact.second_layer_height \
+ self.m1_space + 0.5*self.m1_width) + self.m1_space + 0.5 * self.m1_width)
# This is the width of a m1 extend the ends of the pin # This is the width of a m1 extend the ends of the pin
end_offset = vector(self.m1_width/2,0) 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. # so reverse the directions for NMOS compared to PMOS.
if self.tx_type == "pmos": if self.tx_type == "pmos":
drain_dir = -1 drain_dir = -1
@ -219,17 +238,19 @@ class ptx(design.design):
drain_dir = 1 drain_dir = 1
source_dir = -1 source_dir = -1
if len(source_positions)>1: if len(source_positions) > 1:
source_offset = pin_offset.scale(source_dir,source_dir) source_offset = pin_offset.scale(source_dir,source_dir)
self.remove_layout_pin("S") # remove the individual connections self.remove_layout_pin("S") # remove the individual connections
# Add each vertical segment # Add each vertical segment
for a in source_positions: 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 # Add a single horizontal pin
self.add_layout_pin_segment_center(text="S", self.add_layout_pin_segment_center(text="S",
layer="metal1", layer="metal1",
start=source_positions[0]+source_offset-end_offset, start=source_positions[0] + source_offset - end_offset,
end=source_positions[-1]+source_offset+end_offset) end=source_positions[-1] + source_offset + end_offset)
if len(drain_positions)>1: if len(drain_positions)>1:
drain_offset = pin_offset.scale(drain_dir,drain_dir) drain_offset = pin_offset.scale(drain_dir,drain_dir)
@ -240,24 +261,27 @@ class ptx(design.design):
# Add a single horizontal pin # Add a single horizontal pin
self.add_layout_pin_segment_center(text="D", self.add_layout_pin_segment_center(text="D",
layer="metal1", layer="metal1",
start=drain_positions[0]+drain_offset-end_offset, start=drain_positions[0] + drain_offset - end_offset,
end=drain_positions[-1]+drain_offset+end_offset) end=drain_positions[-1] + drain_offset + end_offset)
def add_poly(self): def add_poly(self):
""" """
Add the poly gates(s) and (optionally) connect them. Add the poly gates(s) and (optionally) connect them.
""" """
# poly is one contacted spacing from the end and down an extension # 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) + vector(self.end_to_poly, -self.poly_extend_active)
# poly_positions are the bottom center of the poly gates # poly_positions are the bottom center of the poly gates
poly_positions = [] 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 # order for the accessors
for i in range(0, self.mults): 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", self.add_rect_center(layer="poly",
offset=poly_offset, offset=poly_offset,
height=self.poly_height, height=self.poly_height,
@ -274,8 +298,8 @@ class ptx(design.design):
self.connect_fingered_poly(poly_positions) self.connect_fingered_poly(poly_positions)
def add_active(self): def add_active(self):
""" """
Adding the diffusion (active region = diffusion region) Adding the diffusion (active region = diffusion region)
""" """
self.add_rect(layer="active", self.add_rect(layer="active",
offset=self.active_offset, offset=self.active_offset,
@ -284,11 +308,11 @@ class ptx(design.design):
# If the implant must enclose the active, shift offset # If the implant must enclose the active, shift offset
# and increase width/height # and increase width/height
enclose_width = drc("implant_enclosure_active") 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), self.add_rect(layer="{}implant".format(self.implant_type),
offset=self.active_offset - enclose_offset, offset=self.active_offset - enclose_offset,
width=self.active_width + 2*enclose_width, width=self.active_width + 2 * enclose_width,
height=self.active_height + 2*enclose_width) height=self.active_height + 2 * enclose_width)
def add_well_implant(self): def add_well_implant(self):
""" """
@ -320,15 +344,16 @@ class ptx(design.design):
# The first one will always be a source # The first one will always be a source
source_positions = [self.contact_offset] source_positions = [self.contact_offset]
drain_positions = [] 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. # order for the accessors.
for i in range(self.mults): for i in range(self.mults):
if i%2: if i%2:
# It's a source... so offset from previous drain. # 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: else:
# It's a drain... so offset from previous source. # 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] return [source_positions,drain_positions]
@ -371,9 +396,12 @@ class ptx(design.design):
def get_cin(self): def get_cin(self):
"""Returns the relative gate cin of the tx""" """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): 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) 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 import debug
from tech import drc from tech import drc
from vector import vector from vector import vector
import contact
from globals import OPTS
from sram_factory import factory from sram_factory import factory
import logical_effort import logical_effort
class single_level_column_mux(pgate.pgate): class single_level_column_mux(pgate.pgate):
""" """
This module implements the columnmux bitline cell used in the design. This module implements the columnmux bitline cell used in the design.
Creates a single columnmux cell with the given integer size relative Creates a single columnmux cell with the given integer size relative
to minimum size. Default is 8x. Per Samira and Hodges-Jackson book: 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"): 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): def create_layout(self):
self.pin_height = 2*self.m2_width self.pin_height = 2 * self.m2_width
self.width = self.bitcell.width self.width = self.bitcell.width
self.height = self.nmos_upper.uy() + self.pin_height self.height = self.nmos_upper.uy() + self.pin_height
self.connect_poly() self.connect_poly()
@ -50,11 +50,9 @@ class single_level_column_mux(pgate.pgate):
self.bitcell = factory.create(module_type="bitcell") self.bitcell = factory.create(module_type="bitcell")
# Adds nmos_lower,nmos_upper to the module # 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.nmos = factory.create(module_type="ptx", width=self.ptx_width)
self.add_mod(self.nmos) self.add_mod(self.nmos)
def add_pins(self): def add_pins(self):
self.add_pin_list(["bl", "br", "bl_out", "br_out", "sel", "gnd"]) 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 # bl and br
self.add_layout_pin(text="bl", self.add_layout_pin(text="bl",
layer="metal2", 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) height=self.pin_height)
self.add_layout_pin(text="br", self.add_layout_pin(text="br",
layer="metal2", 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) height=self.pin_height)
# bl_out and br_out # bl_out and br_out
@ -85,35 +83,34 @@ class single_level_column_mux(pgate.pgate):
offset=br_pos, offset=br_pos,
height=self.pin_height) height=self.pin_height)
def add_ptx(self): def add_ptx(self):
""" Create the two pass gate NMOS transistors to switch the bitlines""" """ Create the two pass gate NMOS transistors to switch the bitlines"""
# Space it in the center # 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) nmos_lower_position = self.nmos.active_offset.scale(0,1) \
self.nmos_lower=self.add_inst(name="mux_tx1", + vector(0.5 * self.bitcell.width- 0.5 * self.nmos.active_width, 0)
mod=self.nmos, self.nmos_lower = self.add_inst(name="mux_tx1",
offset=nmos_lower_position) mod=self.nmos,
offset=nmos_lower_position)
self.connect_inst(["bl", "sel", "bl_out", "gnd"]) self.connect_inst(["bl", "sel", "bl_out", "gnd"])
# This aligns it directly above the other tx with gates abutting # 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) nmos_upper_position = nmos_lower_position \
self.nmos_upper=self.add_inst(name="mux_tx2", + vector(0, self.nmos.active_height + self.poly_space)
mod=self.nmos, self.nmos_upper = self.add_inst(name="mux_tx2",
offset=nmos_upper_position) mod=self.nmos,
offset=nmos_upper_position)
self.connect_inst(["br", "sel", "br_out", "gnd"]) self.connect_inst(["br", "sel", "br_out", "gnd"])
def connect_poly(self): def connect_poly(self):
""" Connect the poly gate of the two pass transistors """ """ 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", self.add_layout_pin(text="sel",
layer="poly", layer="poly",
offset=self.nmos_lower.get_pin("G").ll(), offset=self.nmos_lower.get_pin("G").ll(),
height=height) height=height)
def connect_bitlines(self): def connect_bitlines(self):
""" Connect the bitlines to the mux transistors """ """ Connect the bitlines to the mux transistors """
# These are on metal2 # 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") nmos_upper_d_pin = self.nmos_upper.get_pin("D")
# Add vias to bl, br_out, nmos_upper/S, nmos_lower/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(), offset=bl_pin.bc(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=br_out_pin.uc(), offset=br_out_pin.uc(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_upper_s_pin.center(), offset=nmos_upper_s_pin.center(),
directions=("V","V")) directions=("V", "V"))
self.add_via_center(layers=("metal1","via1","metal2"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=nmos_lower_d_pin.center(), offset=nmos_lower_d_pin.center(),
directions=("V","V")) directions=("V", "V"))
# bl -> nmos_upper/D on metal1 # bl -> nmos_upper/D on metal1
# bl_out -> nmos_upper/S on metal2 # 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 # halfway up, move over
mid1 = bl_out_pin.uc().scale(1,0.4)+nmos_upper_s_pin.bc().scale(0,0.4) mid1 = bl_out_pin.uc().scale(1, 0.4) \
mid2 = bl_out_pin.uc().scale(0,0.4)+nmos_upper_s_pin.bc().scale(1,0.4) + nmos_upper_s_pin.bc().scale(0, 0.4)
self.add_path("metal2",[bl_out_pin.uc(), mid1, mid2, nmos_upper_s_pin.bc()]) 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 -> nmos_lower/D on metal2
# br_out -> nmos_lower/S on metal1 # 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 # halfway up, move over
mid1 = br_pin.bc().scale(1,0.5)+nmos_lower_d_pin.uc().scale(0,0.5) mid1 = br_pin.bc().scale(1,0.5) \
mid2 = br_pin.bc().scale(0,0.5)+nmos_lower_d_pin.uc().scale(1,0.5) + nmos_lower_d_pin.uc().scale(0,0.5)
self.add_path("metal2",[br_pin.bc(), mid1, mid2, nmos_lower_d_pin.uc()]) 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): def add_wells(self):
""" """
Add a well and implant over the whole cell. Also, add the 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 # 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_pos = vector(self.bitcell.width,
active_via = self.add_via_center(layers=("active", "contact", "metal1"), self.nmos_upper.by() - 0.5 * self.poly_space)
offset=active_pos, self.add_via_center(layers=("active", "contact", "metal1"),
implant_type="p", offset=active_pos,
well_type="p") 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"), self.add_via_center(layers=("metal1", "via1", "metal2"),
offset=active_pos) offset=active_pos)
self.add_via_center(layers=("metal2", "via2", "metal3"), 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 # Add well enclosure over all the tx and contact
self.add_rect(layer="pwell", self.add_rect(layer="pwell",
offset=vector(0,0), offset=vector(0, 0),
width=self.bitcell.width, width=self.bitcell.width,
height=self.height) height=self.height)
def get_stage_effort(self, corner, slew, load): 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 parasitic_delay = 1
cin = 2*self.tx_size #This is not CMOS, so using this may be incorrect. # 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) 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) # (acting for and on behalf of Oklahoma State University)
# All rights reserved. # All rights reserved.
# #
import debug
from globals import OPTS from globals import OPTS
class sram_factory: class sram_factory:
""" """
This is a factory pattern to create modules for usage in an SRAM. This is a factory pattern to create modules for usage in an SRAM.
@ -19,7 +19,7 @@ class sram_factory:
""" """
def __init__(self): def __init__(self):
# A dictionary of modules indexed by module type # A dictionary of modules indexed by module type
self.modules = {} self.modules = {}
# These are the indices to append to name to make unique object names # These are the indices to append to name to make unique object names
self.module_indices = {} self.module_indices = {}
@ -34,8 +34,8 @@ class sram_factory:
def create(self, module_type, **kwargs): def create(self, module_type, **kwargs):
""" """
A generic function to create a module with a given module_type. The args A generic function to create a module with a given module_type.
are passed directly to the module constructor. The args are passed directly to the module constructor.
""" """
# if name!="": # if name!="":
# # This is a special case where the name and type don't match # # This is a special case where the name and type don't match
@ -58,28 +58,30 @@ class sram_factory:
self.objects[module_type] = [] self.objects[module_type] = []
# Either retreive a previous object or create a new one # Either retreive a previous object or create a new one
#print("new",kwargs)
for obj in self.objects[module_type]: for obj in self.objects[module_type]:
(obj_kwargs, obj_item) = obj (obj_kwargs, obj_item) = obj
# Must have the same dictionary exactly (conservative) # Must have the same dictionary exactly (conservative)
if obj_kwargs == kwargs: 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 return obj_item
#else:
# print("obj",obj_kwargs)
# Use the default name if there are default arguments # 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. # This is especially for library cells so that the
if len(kwargs)>0: # spice and gds files can be found.
if len(kwargs) > 0:
# Create a unique name and increment the index # 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 self.module_indices[module_type] += 1
else: else:
module_name = module_type module_name = module_type
#debug.info(0, "New module: type={0} name={1} kwargs={2}".format(module_type,module_name,str(kwargs))) # type_str = "type={}".format(module_type)
obj = mod(name=module_name,**kwargs) # name_str = "name={}".format(module_name)
self.objects[module_type].append((kwargs,obj)) # 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 return obj
def get_mods(self, module_type): def get_mods(self, module_type):
@ -90,11 +92,11 @@ class sram_factory:
module_type = getattr(OPTS, module_type) module_type = getattr(OPTS, module_type)
try: try:
mod_tuples = self.objects[module_type] mod_tuples = self.objects[module_type]
mods = [mod for kwargs,mod in mod_tuples] mods = [mod for kwargs, mod in mod_tuples]
except KeyError: except KeyError:
mods = [] mods = []
return mods return mods
# Make a factory # Make a factory
factory = sram_factory() factory = sram_factory()

View File

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

View File

@ -65,16 +65,16 @@ class timing_sram_test(openram_test):
'write0_power': [0.36360849999999995], 'write0_power': [0.36360849999999995],
'write1_power': [0.3486931]} 'write1_power': [0.3486931]}
elif OPTS.tech_name == "scn4m_subm": elif OPTS.tech_name == "scn4m_subm":
golden_data = {'delay_hl': [1.7083549999999998], golden_data = {'delay_hl': [1.85985],
'delay_lh': [1.7083549999999998], 'delay_lh': [1.85985],
'leakage_power': 0.001119657, 'leakage_power': 0.008613619,
'min_period': 7.812, 'min_period': 6.875,
'read0_power': [8.013845], 'read0_power': [12.656310000000001],
'read1_power': [7.6889389999999995], 'read1_power': [12.11682],
'slew_hl': [1.31918], 'slew_hl': [1.868942],
'slew_lh': [1.31918], 'slew_lh': [1.868942],
'write0_power': [8.791557000000001], 'write0_power': [13.978110000000001],
'write1_power': [8.70443]} 'write1_power': [11.437930000000001]}
else: else:
self.assertTrue(False) # other techs fail 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_HL': [0.02685547],
'setup_times_LH': [0.03295898]} 'setup_times_LH': [0.03295898]}
elif OPTS.tech_name == "scn4m_subm": elif OPTS.tech_name == "scn4m_subm":
golden_data = {'hold_times_HL': [-0.08911132999999999], golden_data = {'hold_times_HL': [-0.08056640999999999],
'hold_times_LH': [-0.0769043], 'hold_times_LH': [-0.1293945],
'setup_times_HL': [0.1184082], 'setup_times_HL': [0.1757812],
'setup_times_LH': [0.1672363]} 'setup_times_LH': [0.1879883]}
else: else:
self.assertTrue(False) # other techs fail self.assertTrue(False) # other techs fail

View File

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

View File

@ -5,6 +5,36 @@
* models from MOSIS or SCN4ME * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845 .MODEL n NMOS ( LEVEL = 49
+ NSUB=6E16 U0=461 K1=0.5705 TOX=13.9n VERSION=3.3.0) +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 * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431 .MODEL p PMOS ( LEVEL = 49
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0) +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 * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845 .MODEL n NMOS ( LEVEL = 49
+ NSUB=6E16 U0=458 K1=0.5705 TOX=13.9n VERSION=3.3.0) +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 * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431 .MODEL p PMOS ( LEVEL = 49
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0) +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 * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL n NMOS (LEVEL=49 VTHO=0.669845 .MODEL n NMOS ( LEVEL = 49
+ NSUB=6E16 U0=460 K1=0.5705 TOX=13.9n VERSION=3.3.0) +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 * models from MOSIS or SCN4ME
********************************************* *********************************************
.MODEL p PMOS (LEVEL=49 VTHO=-0.322431 .MODEL p PMOS ( LEVEL = 49
+ NSUB=6E16 U0=212 K1=0.0821 TOX=13.9n VERSION=3.3.0) +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 spice["default_event_frequency"] = 100 # Default event activity of every gate. MHz
#Logical Effort relative values for the Handmade cells #Logical Effort relative values for the Handmade cells
parameter["le_tau"] = 23 #In pico-seconds. parameter["le_tau"] = 18.17 #In pico-seconds.
parameter["min_inv_para_delay"] = .73 #In relative delay units parameter["min_inv_para_delay"] = 2.07 #In relative delay units
parameter["cap_relative_per_ff"] = .91 #Units of Relative Capacitance/ Femto-Farad parameter["cap_relative_per_ff"] = .91 #Units of Relative Capacitance/ Femto-Farad
parameter["dff_clk_cin"] = 27.5 #In relative capacitance units parameter["dff_clk_cin"] = 27.5 #In relative capacitance units
parameter["6tcell_wl_cin"] = 2 #In relative capacitance units parameter["6tcell_wl_cin"] = 2 #In relative capacitance units