Merge branch 'dev' into update_scmos_models

This commit is contained in:
Hunter Nichols 2019-10-03 13:16:32 -07:00
commit d722311822
10 changed files with 199 additions and 174 deletions

View File

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

View File

@ -6,6 +6,7 @@
# All rights reserved.
#
class delay_data():
"""
This is the delay class to represent the delay information
@ -20,7 +21,7 @@ class delay_data():
def __str__(self):
""" override print function output """
return "Delay Data: Delay "+str(self.delay)+", Slew "+str(self.slew)+""
return "Delta Data: Delay {} Slew {}".format(self.delay, self.slew)
def __add__(self, other):
"""

View File

@ -7,12 +7,9 @@
#
from hierarchy_design import hierarchy_design
import contact
import globals
import verify
import debug
import os
from globals import OPTS
class design(hierarchy_design):
"""
This is the same as the hierarchy_design class except it contains

View File

@ -15,6 +15,7 @@ import math
from globals import OPTS
from utils import round_to_grid
class geometry:
"""
A specific path, shape, or text geometry. Base class for shared
@ -54,8 +55,10 @@ class geometry:
def normalize(self):
""" Re-find the LL and UR points after a transform """
(first, second) = self.boundary
ll = vector(min(first[0],second[0]),min(first[1],second[1])).snap_to_grid()
ur = vector(max(first[0],second[0]),max(first[1],second[1])).snap_to_grid()
ll = vector(min(first[0], second[0]),
min(first[1], second[1])).snap_to_grid()
ur = vector(max(first[0], second[0]),
max(first[1], second[1])).snap_to_grid()
self.boundary = [ll, ur]
def update_boundary(self):
@ -108,7 +111,6 @@ class geometry:
""" Return the upper left corner """
return vector(self.boundary[0].x, self.boundary[1].y)
def uy(self):
""" Return the upper edge """
return self.boundary[1].y
@ -142,7 +144,8 @@ class instance(geometry):
def __init__(self, name, mod, offset=[0, 0], mirror="R0", rotate=0):
"""Initializes an instance to represent a module"""
geometry.__init__(self)
debug.check(mirror not in ["R90","R180","R270"], "Please use rotation and not mirroring during instantiation.")
debug.check(mirror not in ["R90", "R180", "R270"],
"Please use rotation and not mirroring during instantiation.")
self.name = name
self.mod = mod
@ -339,6 +342,7 @@ class label(geometry):
""" override print function output """
return "( label: " + self.text + " @" + str(self.offset) + " layer=" + str(self.layerNumber) + " )"
class rectangle(geometry):
"""Represents a rectangular shape"""
@ -356,11 +360,12 @@ class rectangle(geometry):
debug.info(4, "creating rectangle (" + str(self.layerNumber) + "): "
+ str(self.width) + "x" + str(self.height) + " @ " + str(self.offset))
def get_blockages(self, layer):
""" Returns a list of one rectangle if it is on this layer"""
if self.layerNumber == layer:
return [[self.offset, vector(self.offset.x+self.width,self.offset.y+self.height)]]
return [[self.offset,
vector(self.offset.x + self.width,
self.offset.y + self.height)]]
else:
return []

View File

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

View File

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

View File

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

View File

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

View File

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