merging changes with pbitcell_array test

This commit is contained in:
Michael Timothy Grimes 2018-07-12 23:51:44 -07:00
commit ba43b986ae
85 changed files with 1181 additions and 1076 deletions

View File

@ -1,11 +1,11 @@
import design import hierarchy_design
import debug import debug
import utils import utils
from tech import drc from tech import drc
from vector import vector from vector import vector
class contact(design.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 a contact array minimum active or poly enclosure and metal1 enclosure. Creates a contact array minimum active or poly enclosure and metal1 enclosure.
@ -31,7 +31,7 @@ class contact(design.design):
dimensions[0], dimensions[0],
dimensions[1]) dimensions[1])
design.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.layer_stack = layer_stack self.layer_stack = layer_stack
@ -158,6 +158,9 @@ class contact(design.design):
width=well_width, width=well_width,
height=well_height) height=well_height)
def analytical_power(self, proc, vdd, temp, load):
""" Get total power of a module """
return self.return_power()
# This is not instantiated and used for calculations only. # This is not instantiated and used for calculations only.
@ -167,4 +170,5 @@ active = contact(layer_stack=("active", "contact", "poly"))
poly = contact(layer_stack=("poly", "contact", "metal1")) poly = contact(layer_stack=("poly", "contact", "metal1"))
m1m2 = contact(layer_stack=("metal1", "via1", "metal2")) m1m2 = contact(layer_stack=("metal1", "via1", "metal2"))
m2m3 = contact(layer_stack=("metal2", "via2", "metal3")) m2m3 = contact(layer_stack=("metal2", "via2", "metal3"))
#m3m4 = contact(layer_stack=("metal3", "via3", "metal4"))

View File

@ -1,5 +1,5 @@
import hierarchy_layout from hierarchy_design import hierarchy_design
import hierarchy_spice import contact
import globals import globals
import verify import verify
import debug import debug
@ -7,46 +7,24 @@ import os
from globals import OPTS from globals import OPTS
class design(hierarchy_spice.spice, hierarchy_layout.layout): class design(hierarchy_design):
""" """
Design Class for all modules to inherit the base features. This is the same as the hierarchy_design class except it contains
Class consisting of a set of modules and instances of these modules some DRC constants and analytical models for other modules to reuse.
""" """
name_map = []
def __init__(self, name): def __init__(self, name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds" hierarchy_design.__init__(self,name)
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
self.name = name
hierarchy_layout.layout.__init__(self, name)
hierarchy_spice.spice.__init__(self, name)
self.setup_drc_constants() self.setup_drc_constants()
# Check if the name already exists, if so, give an error self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space, self.m2_space)
# because each reference must be a unique name. self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space, self.m3_space)
# These modules ensure unique names or have no changes if they # SCMOS doesn't have m4...
# aren't unique #self.m3_pitch = max(contact.m3m4.width,contact.m3m4.height) + max(self.m3_space, self.m4_space)
ok_list = ['ms_flop', self.m3_pitch = self.m2_pitch
'dff',
'dff_buf',
'bitcell',
'contact',
'ptx',
'sram',
'hierarchical_predecode2x4',
'hierarchical_predecode3x8']
if name not in design.name_map:
design.name_map.append(name)
else:
for ok_names in ok_list:
if ok_names in self.__class__.__name__:
break
else:
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)
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 from tech import drc
@ -61,70 +39,14 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
self.m3_space = drc["metal3_to_metal3"] self.m3_space = drc["metal3_to_metal3"]
self.active_width = drc["minwidth_active"] self.active_width = drc["minwidth_active"]
self.contact_width = drc["minwidth_contact"] self.contact_width = drc["minwidth_contact"]
self.poly_to_active = drc["poly_to_active"] self.poly_to_active = drc["poly_to_active"]
self.poly_extend_active = drc["poly_extend_active"] self.poly_extend_active = drc["poly_extend_active"]
self.contact_to_gate = drc["contact_to_gate"] self.contact_to_gate = drc["contact_to_gate"]
self.well_enclose_active = drc["well_enclosure_active"] self.well_enclose_active = drc["well_enclosure_active"]
self.implant_enclose_active = drc["implant_enclosure_active"] self.implant_enclose_active = drc["implant_enclosure_active"]
self.implant_space = drc["implant_to_implant"] self.implant_space = drc["implant_to_implant"]
def get_layout_pins(self,inst):
""" Return a map of pin locations of the instance offset """
# find the instance
for i in self.insts:
if i.name == inst.name:
break
else:
debug.error("Couldn't find instance {0}".format(inst_name),-1)
inst_map = inst.mod.pin_map
return inst_map
def DRC_LVS(self, final_verification=False):
"""Checks both DRC and LVS for a module"""
if OPTS.check_lvsdrc:
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
self.sp_write(tempspice)
self.gds_write(tempgds)
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
os.remove(tempspice)
os.remove(tempgds)
def DRC(self):
"""Checks DRC for a module"""
if OPTS.check_lvsdrc:
tempgds = OPTS.openram_temp + "/temp.gds"
self.gds_write(tempgds)
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
os.remove(tempgds)
def LVS(self, final_verification=False):
"""Checks LVS for a module"""
if OPTS.check_lvsdrc:
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
self.sp_write(tempspice)
self.gds_write(tempgds)
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
os.remove(tempspice)
os.remove(tempgds)
def __str__(self):
""" override print function output """
return "design: " + self.name
def __repr__(self):
""" override print function output """
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
for i in self.objs:
text+=str(i)+",\n"
for i in self.insts:
text+=str(i)+",\n"
return text
def analytical_power(self, proc, vdd, temp, load): def analytical_power(self, proc, vdd, temp, load):
""" Get total power of a module """ """ Get total power of a module """
total_module_power = self.return_power() total_module_power = self.return_power()

View File

@ -0,0 +1,111 @@
import hierarchy_layout
import hierarchy_spice
import globals
import verify
import debug
import os
from globals import OPTS
class hierarchy_design(hierarchy_spice.spice, hierarchy_layout.layout):
"""
Design Class for all modules to inherit the base features.
Class consisting of a set of modules and instances of these modules
"""
name_map = []
def __init__(self, name):
self.gds_file = OPTS.openram_tech + "gds_lib/" + name + ".gds"
self.sp_file = OPTS.openram_tech + "sp_lib/" + name + ".sp"
self.name = name
hierarchy_layout.layout.__init__(self, name)
hierarchy_spice.spice.__init__(self, name)
# Check if the name already exists, if so, give an error
# because each reference must be a unique name.
# These modules ensure unique names or have no changes if they
# aren't unique
ok_list = ['ms_flop',
'dff',
'dff_buf',
'bitcell',
'contact',
'ptx',
'sram',
'hierarchical_predecode2x4',
'hierarchical_predecode3x8']
if name not in hierarchy_design.name_map:
hierarchy_design.name_map.append(name)
else:
for ok_names in ok_list:
if ok_names in self.__class__.__name__:
break
else:
debug.error("Duplicate layout reference name {0} of class {1}. GDS2 requires names be unique.".format(name,self.__class__),-1)
def get_layout_pins(self,inst):
""" Return a map of pin locations of the instance offset """
# find the instance
for i in self.insts:
if i.name == inst.name:
break
else:
debug.error("Couldn't find instance {0}".format(inst_name),-1)
inst_map = inst.mod.pin_map
return inst_map
def DRC_LVS(self, final_verification=False):
"""Checks both DRC and LVS for a module"""
# Unit tests will check themselves.
# Do not run if disabled in options.
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
self.sp_write(tempspice)
self.gds_write(tempgds)
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
os.remove(tempspice)
os.remove(tempgds)
def DRC(self):
"""Checks DRC for a module"""
# Unit tests will check themselves.
# Do not run if disabled in options.
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
tempgds = OPTS.openram_temp + "/temp.gds"
self.gds_write(tempgds)
debug.check(verify.run_drc(self.name, tempgds) == 0,"DRC failed for {0}".format(self.name))
os.remove(tempgds)
def LVS(self, final_verification=False):
"""Checks LVS for a module"""
# Unit tests will check themselves.
# Do not run if disabled in options.
if not OPTS.is_unit_test and OPTS.check_lvsdrc:
tempspice = OPTS.openram_temp + "/temp.sp"
tempgds = OPTS.openram_temp + "/temp.gds"
self.sp_write(tempspice)
self.gds_write(tempgds)
debug.check(verify.run_lvs(self.name, tempgds, tempspice, final_verification) == 0,"LVS failed for {0}".format(self.name))
os.remove(tempspice)
os.remove(tempgds)
def __str__(self):
""" override print function output """
return "design: " + self.name
def __repr__(self):
""" override print function output """
text="( design: " + self.name + " pins=" + str(self.pins) + " " + str(self.width) + "x" + str(self.height) + " )\n"
for i in self.objs:
text+=str(i)+",\n"
for i in self.insts:
text+=str(i)+",\n"
return text

View File

@ -524,6 +524,62 @@ class layout(lef.lef):
return blockages return blockages
def create_horizontal_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=True)
def create_vertical_pin_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus of pins. """
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=True)
def create_vertical_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus. """
self.create_bus(layer,pitch,offset,names,length,vertical=True,make_pins=False)
def create_horiontal_bus(self, layer, pitch, offset, names, length):
""" Create a horizontal bus. """
self.create_bus(layer,pitch,offset,names,length,vertical=False,make_pins=False)
def create_bus(self, layer, pitch, offset, names, length, vertical, make_pins):
"""
Create a horizontal or vertical bus. It can be either just rectangles, or actual
layout pins. It returns an map of line center line positions indexed by name.
"""
# half minwidth so we can return the center line offsets
half_minwidth = 0.5*drc["minwidth_{}".format(layer)]
line_positions = {}
if vertical:
for i in range(len(names)):
line_offset = offset + vector(i*pitch,0)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
height=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
height=length)
line_positions[names[i]]=line_offset+vector(half_minwidth,0)
else:
for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
width=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
width=length)
line_positions[names[i]]=line_offset+vector(0,half_minwidth)
return line_positions
def add_enclosure(self, insts, layer="nwell"): def add_enclosure(self, insts, layer="nwell"):
""" Add a layer that surrounds the given instances. Useful """ Add a layer that surrounds the given instances. Useful
for creating wells, for example. Doesn't check for minimum widths or for creating wells, for example. Doesn't check for minimum widths or

View File

@ -1,16 +1,18 @@
import os import os
import debug import debug
import globals
from globals import OPTS,find_exe,get_tool from globals import OPTS,find_exe,get_tool
from .lib import * from .lib import *
from .delay import * from .delay import *
from .setup_hold import * from .setup_hold import *
debug.info(2,"Initializing characterizer...") debug.info(1,"Initializing characterizer...")
OPTS.spice_exe = "" OPTS.spice_exe = ""
if not OPTS.analytical_delay: if not OPTS.analytical_delay:
debug.info(1, "Finding spice simulator.")
if OPTS.spice_name != "": if OPTS.spice_name != "":
OPTS.spice_exe=find_exe(OPTS.spice_name) OPTS.spice_exe=find_exe(OPTS.spice_name)
if OPTS.spice_exe=="": if OPTS.spice_exe=="":
@ -24,6 +26,7 @@ if not OPTS.analytical_delay:
if OPTS.spice_exe == "": if OPTS.spice_exe == "":
debug.error("No recognizable spice version found. Unable to perform characterization.",1) debug.error("No recognizable spice version found. Unable to perform characterization.",1)
else:
debug.info(1,"Analytical model enabled.")

View File

@ -13,19 +13,19 @@ def check(check,str):
(frame, filename, line_number, function_name, lines, (frame, filename, line_number, function_name, lines,
index) = inspect.getouterframes(inspect.currentframe())[1] index) = inspect.getouterframes(inspect.currentframe())[1]
if not check: if not check:
print("ERROR: file {0}: line {1}: {2}".format(os.path.basename(filename),line_number,str)) sys.stderr.write("ERROR: file {0}: line {1}: {2}\n".format(os.path.basename(filename),line_number,str))
assert 0 assert 0
def error(str,return_value=0): def error(str,return_value=0):
(frame, filename, line_number, function_name, lines, (frame, filename, line_number, function_name, lines,
index) = inspect.getouterframes(inspect.currentframe())[1] index) = inspect.getouterframes(inspect.currentframe())[1]
print("ERROR: file {0}: line {1}: {2}".format(os.path.basename(filename),line_number,str)) sys.stderr.write("ERROR: file {0}: line {1}: {2}\n".format(os.path.basename(filename),line_number,str))
assert return_value==0 assert return_value==0
def warning(str): def warning(str):
(frame, filename, line_number, function_name, lines, (frame, filename, line_number, function_name, lines,
index) = inspect.getouterframes(inspect.currentframe())[1] index) = inspect.getouterframes(inspect.currentframe())[1]
print("WARNING: file {0}: line {1}: {2}".format(os.path.basename(filename),line_number,str)) sys.stderr.write("WARNING: file {0}: line {1}: {2}\n".format(os.path.basename(filename),line_number,str))
def info(lev, str): def info(lev, str):

View File

@ -9,12 +9,14 @@ import optparse
import options import options
import sys import sys
import re import re
import copy
import importlib import importlib
USAGE = "Usage: openram.py [options] <config file>\nUse -h for help.\n" USAGE = "Usage: openram.py [options] <config file>\nUse -h for help.\n"
# Anonymous object that will be the options # Anonymous object that will be the options
OPTS = options.options() OPTS = options.options()
CHECKPOINT_OPTS=None
def parse_args(): def parse_args():
""" Parse the optional arguments for OpenRAM """ """ Parse the optional arguments for OpenRAM """
@ -102,6 +104,8 @@ def check_versions():
def init_openram(config_file, is_unit_test=True): def init_openram(config_file, is_unit_test=True):
"""Initialize the technology, paths, simulators, etc.""" """Initialize the technology, paths, simulators, etc."""
check_versions() check_versions()
debug.info(1,"Initializing OpenRAM...") debug.info(1,"Initializing OpenRAM...")
@ -112,6 +116,30 @@ def init_openram(config_file, is_unit_test=True):
import_tech() import_tech()
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[]
global OPTS
global CHECKPOINT_OPTS
# This is a hack. If we are running a unit test and have checkpointed
# the options, load them rather than reading the config file.
# This way, the configuration is reloaded at the start of every unit test.
# If a unit test fails, we don't have to worry about restoring the old config values
# that may have been tested.
if is_unit_test and CHECKPOINT_OPTS:
OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy()
return
# Import these to find the executables for checkpointing
import characterizer
import verify
# Make a checkpoint of the options so we can restore
# after each unit test
if not CHECKPOINT_OPTS:
CHECKPOINT_OPTS = copy.copy(OPTS)
def get_tool(tool_type, preferences): def get_tool(tool_type, preferences):
@ -132,15 +160,15 @@ def get_tool(tool_type, preferences):
return(None,"") return(None,"")
def read_config(config_file, is_unit_test=True): def read_config(config_file, is_unit_test=True):
""" """
Read the configuration file that defines a few parameters. The Read the configuration file that defines a few parameters. The
config file is just a Python file that defines some config config file is just a Python file that defines some config
options. options. This will only actually get read the first time. Subsequent
reads will just restore the previous copy (ask mrg)
""" """
global OPTS global OPTS
# Create a full path relative to current dir unless it is already an abs path # Create a full path relative to current dir unless it is already an abs path
if not os.path.isabs(config_file): if not os.path.isabs(config_file):
config_file = os.getcwd() + "/" + config_file config_file = os.getcwd() + "/" + config_file
@ -164,6 +192,7 @@ def read_config(config_file, is_unit_test=True):
# The command line will over-ride the config file # The command line will over-ride the config file
# except in the case of the tech name! This is because the tech name # except in the case of the tech name! This is because the tech name
# is sometimes used to specify the config file itself (e.g. unit tests) # is sometimes used to specify the config file itself (e.g. unit tests)
# Note that if we re-read a config file, nothing will get read again!
if not k in OPTS.__dict__ or k=="tech_name": if not k in OPTS.__dict__ or k=="tech_name":
OPTS.__dict__[k]=v OPTS.__dict__[k]=v
@ -192,12 +221,16 @@ def read_config(config_file, is_unit_test=True):
os.chmod(OPTS.output_path, 0o750) os.chmod(OPTS.output_path, 0o750)
except: except:
debug.error("Unable to make output directory.",-1) debug.error("Unable to make output directory.",-1)
def end_openram(): def end_openram():
""" Clean up openram for a proper exit """ """ Clean up openram for a proper exit """
cleanup_paths() cleanup_paths()
import verify
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()
@ -206,6 +239,7 @@ def cleanup_paths():
""" """
We should clean up the temp directory after execution. We should clean up the temp directory after execution.
""" """
global OPTS
if not OPTS.purge_temp: if not OPTS.purge_temp:
debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp)) debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp))
return return
@ -275,9 +309,6 @@ def import_tech():
debug.info(2,"Importing technology: " + OPTS.tech_name) debug.info(2,"Importing technology: " + OPTS.tech_name)
# Set the tech to the config file we read in instead of the command line value.
OPTS.tech_name = OPTS.tech_name
# environment variable should point to the technology dir # environment variable should point to the technology dir
try: try:
OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH")) OPENRAM_TECH = os.path.abspath(os.environ.get("OPENRAM_TECH"))
@ -323,6 +354,8 @@ def print_time(name, now_time, last_time=None):
def report_status(): def report_status():
""" Check for valid arguments and report the info about the SRAM being generated """ """ Check for valid arguments and report the info about the SRAM being generated """
global OPTS
# Check if all arguments are integers for bits, size, banks # Check if all arguments are integers for bits, size, banks
if type(OPTS.word_size)!=int: if type(OPTS.word_size)!=int:
debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) debug.error("{0} is not an integer in config file.".format(OPTS.word_size))

View File

@ -161,10 +161,6 @@ class bank(design.design):
else: else:
self.num_col_addr_lines = 0 self.num_col_addr_lines = 0
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space)
self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
# The width of this bus is needed to place other modules (e.g. decoder) # The width of this bus is needed to place other modules (e.g. decoder)
# A width on each side too # A width on each side too
self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width self.central_bus_width = self.m2_pitch * self.num_control_lines + 2*self.m2_width

View File

@ -56,10 +56,6 @@ class bank_select(design.design):
def calculate_module_offsets(self): def calculate_module_offsets(self):
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = contact.m1m2.height + max(self.m1_space,self.m2_space)
self.m2_pitch = contact.m2m3.height + max(self.m2_space,self.m3_space)
self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] self.xoffset_nand = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"]
self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"] self.xoffset_nor = self.inv4x.width + 2*self.m2_pitch + drc["pwell_to_nwell"]
self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width) self.xoffset_inv = max(self.xoffset_nand + self.nand2.width, self.xoffset_nor + self.nor2.width)

View File

@ -86,10 +86,6 @@ class control_logic(design.design):
# These aren't for instantiating, but we use them to get the dimensions # These aren't for instantiating, but we use them to get the dimensions
#self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height) #self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# M1/M2 routing pitch is based on contacted pitch
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(drc["metal2_to_metal2"],drc["metal3_to_metal3"])
# Have the cell gap leave enough room to route an M2 wire. # Have the cell gap leave enough room to route an M2 wire.
# Some cells may have pwell/nwell spacing problems too when the wells are different heights. # Some cells may have pwell/nwell spacing problems too when the wells are different heights.
#self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"]) #self.cell_gap = max(self.m2_pitch,drc["pwell_to_nwell"])

View File

@ -45,7 +45,8 @@ class hierarchical_decoder(design.design):
self.add_pins() self.add_pins()
self.create_pre_decoder() self.create_pre_decoder()
self.create_row_decoder() self.create_row_decoder()
self.create_vertical_rail() self.create_input_rail()
self.create_predecode_rail()
self.route_vdd_gnd() self.route_vdd_gnd()
def add_modules(self): def add_modules(self):
@ -60,7 +61,6 @@ class hierarchical_decoder(design.design):
def add_decoders(self): def add_decoders(self):
""" Create the decoders based on the number of pre-decodes """ """ Create the decoders based on the number of pre-decodes """
# FIXME: Only add these if needed?
self.pre2_4 = pre2x4() self.pre2_4 = pre2x4()
self.add_mod(self.pre2_4) self.add_mod(self.pre2_4)
@ -68,8 +68,8 @@ class hierarchical_decoder(design.design):
self.add_mod(self.pre3_8) self.add_mod(self.pre3_8)
def determine_predecodes(self,num_inputs): def determine_predecodes(self,num_inputs):
"""Determines the number of 2:4 pre-decoder and 3:8 pre-decoder """ Determines the number of 2:4 pre-decoder and 3:8 pre-decoder
needed based on the number of inputs""" needed based on the number of inputs """
if (num_inputs == 2): if (num_inputs == 2):
return (1,0) return (1,0)
elif (num_inputs == 3): elif (num_inputs == 3):
@ -90,12 +90,6 @@ class hierarchical_decoder(design.design):
debug.error("Invalid number of inputs for hierarchical decoder",-1) debug.error("Invalid number of inputs for hierarchical decoder",-1)
def setup_layout_constants(self): def setup_layout_constants(self):
# Vertical metal rail gap definition
self.metal2_extend_contact = (contact.m1m2.second_layer_height - contact.m1m2.contact_width) / 2
self.metal2_spacing = self.metal2_extend_contact + self.m2_space
self.metal2_pitch = self.metal2_spacing + self.m2_width
self.via_shift = (contact.m1m2.second_layer_width - contact.m1m2.first_layer_width) / 2
self.predec_groups = [] # This array is a 2D array. self.predec_groups = [] # This array is a 2D array.
# Distributing vertical rails to different groups. One group belongs to one pre-decoder. # Distributing vertical rails to different groups. One group belongs to one pre-decoder.
@ -120,7 +114,75 @@ class hierarchical_decoder(design.design):
self.calculate_dimensions() self.calculate_dimensions()
def create_input_rail(self):
""" Create input rails for the predecoders """
# inputs should be as high as the decoders
input_height = self.no_of_pre2x4*self.pre2_4.height + self.no_of_pre3x8*self.pre3_8.height
# Find the left-most predecoder
min_x = 0
if self.no_of_pre2x4 > 0:
min_x = min(min_x, -self.pre2_4.width)
if self.no_of_pre3x8 > 0:
min_x = min(min_x, -self.pre3_8.width)
input_offset=vector(min_x - self.input_routing_width,0)
input_bus_names = ["A[{0}]".format(i) for i in range(self.num_inputs)]
self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch,
offset=input_offset,
names=input_bus_names,
length=input_height)
self.connect_input_to_predecodes()
def connect_input_to_predecodes(self):
""" Connect the vertical input rail to the predecoders """
for pre_num in range(self.no_of_pre2x4):
for i in range(2):
index = pre_num * 2 + i
input_pin = self.get_pin("A[{}]".format(index))
in_name = "in[{}]".format(i)
decoder_pin = self.pre2x4_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
for pre_num in range(self.no_of_pre3x8):
for i in range(3):
index = pre_num * 3 + i + self.no_of_pre2x4 * 2
input_pin = self.get_pin("A[{}]".format(index))
in_name = "in[{}]".format(i)
decoder_pin = self.pre3x8_inst[pre_num].get_pin(in_name)
# Offset each decoder pin up so they don't conflit
decoder_offset = decoder_pin.center() + vector(0,i*self.m2_pitch)
input_offset = input_pin.center().scale(1,0) + decoder_offset.scale(0,1)
self.connect_input_rail(decoder_offset, input_offset)
def connect_input_rail(self, input_offset, output_offset):
""" Connect a vertical M2 coordinate to another vertical M2 coordinate to the predecode inputs """
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=input_offset,
rotate=90)
self.add_via_center(layers=("metal2", "via2", "metal3"),
offset=output_offset,
rotate=90)
self.add_path(("metal3"), [input_offset, output_offset])
def add_pins(self): def add_pins(self):
""" Add the module pins """ """ Add the module pins """
@ -143,10 +205,11 @@ class hierarchical_decoder(design.design):
debug.error("Not enough rows for a hierarchical decoder. Non-hierarchical not supported yet.",-1) debug.error("Not enough rows for a hierarchical decoder. Non-hierarchical not supported yet.",-1)
# Calculates height and width of pre-decoder, # Calculates height and width of pre-decoder,
if(self.no_of_pre3x8 > 0): if self.no_of_pre3x8 > 0:
self.predecoder_width = self.pre3_8.width self.predecoder_width = self.pre3_8.width
else: else:
self.predecoder_width = self.pre2_4.width self.predecoder_width = self.pre2_4.width
self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8 self.predecoder_height = self.pre2_4.height*self.no_of_pre2x4 + self.pre3_8.height*self.no_of_pre3x8
# Calculates height and width of row-decoder # Calculates height and width of row-decoder
@ -154,12 +217,14 @@ class hierarchical_decoder(design.design):
nand_width = self.nand2.width nand_width = self.nand2.width
else: else:
nand_width = self.nand3.width nand_width = self.nand3.width
self.routing_width = self.metal2_pitch*self.total_number_of_predecoder_outputs self.internal_routing_width = self.m2_pitch*self.total_number_of_predecoder_outputs
self.row_decoder_height = self.inv.height * self.rows self.row_decoder_height = self.inv.height * self.rows
self.input_routing_width = (self.num_inputs+1) * self.m2_pitch
# Calculates height and width of hierarchical decoder # Calculates height and width of hierarchical decoder
self.height = self.row_decoder_height self.height = self.row_decoder_height
self.width = self.predecoder_width + self.routing_width + nand_width + self.inv.width self.width = self.input_routing_width + self.predecoder_width \
+ self.internal_routing_width + nand_width + self.inv.width
def create_pre_decoder(self): def create_pre_decoder(self):
""" Creates pre-decoder and places labels input address [A] """ """ Creates pre-decoder and places labels input address [A] """
@ -171,7 +236,7 @@ class hierarchical_decoder(design.design):
self.add_pre3x8(i) self.add_pre3x8(i)
def add_pre2x4(self,num): def add_pre2x4(self,num):
""" Add a 2x4 predecoder """ """ Add a 2x4 predecoder to the left of the origin """
if (self.num_inputs == 2): if (self.num_inputs == 2):
base = vector(-self.pre2_4.width,0) base = vector(-self.pre2_4.width,0)
@ -193,27 +258,9 @@ class hierarchical_decoder(design.design):
offset=base)) offset=base))
self.connect_inst(pins) self.connect_inst(pins)
self.add_pre2x4_pins(num)
def add_pre2x4_pins(self,num):
""" Add the input pins to the 2x4 predecoder """
for i in range(2):
pin = self.pre2x4_inst[num].get_pin("in[{}]".format(i))
pin_offset = pin.ll()
pin = self.pre2_4.get_pin("in[{}]".format(i))
self.add_layout_pin(text="A[{0}]".format(i + 2*num ),
layer="metal2",
offset=pin_offset,
width=pin.width(),
height=pin.height())
def add_pre3x8(self,num): def add_pre3x8(self,num):
""" Add 3x8 numbered predecoder """ """ Add 3x8 predecoder to the left of the origin and above any 2x4 decoders """
if (self.num_inputs == 3): if (self.num_inputs == 3):
offset = vector(-self.pre_3_8.width,0) offset = vector(-self.pre_3_8.width,0)
mirror ="R0" mirror ="R0"
@ -238,20 +285,6 @@ class hierarchical_decoder(design.design):
offset=offset)) offset=offset))
self.connect_inst(pins) self.connect_inst(pins)
# The 3x8 predecoders will be stacked, so use yoffset
self.add_pre3x8_pins(num,offset)
def add_pre3x8_pins(self,num,offset):
""" Add the input pins to the 3x8 predecoder at the given offset """
for i in range(3):
pin = self.pre3x8_inst[num].get_pin("in[{}]".format(i))
pin_offset = pin.ll()
self.add_layout_pin(text="A[{0}]".format(i + 3*num + 2*self.no_of_pre2x4),
layer="metal2",
offset=pin_offset,
width=pin.width(),
height=pin.height())
@ -313,7 +346,7 @@ class hierarchical_decoder(design.design):
self.nand_inst.append(self.add_inst(name=name, self.nand_inst.append(self.add_inst(name=name,
mod=nand_mod, mod=nand_mod,
offset=[self.routing_width, y_off], offset=[self.internal_routing_width, y_off],
mirror=mirror)) mirror=mirror))
@ -325,9 +358,9 @@ class hierarchical_decoder(design.design):
z_pin = self.inv.get_pin("Z") z_pin = self.inv.get_pin("Z")
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
x_off = self.routing_width + self.nand2.width x_off = self.internal_routing_width + self.nand2.width
else: else:
x_off = self.routing_width + self.nand3.width x_off = self.internal_routing_width + self.nand3.width
self.inv_inst = [] self.inv_inst = []
for row in range(self.rows): for row in range(self.rows):
@ -377,7 +410,7 @@ class hierarchical_decoder(design.design):
def create_vertical_rail(self): def create_predecode_rail(self):
""" Creates vertical metal 2 rails to connect predecoder and decoder stages.""" """ Creates vertical metal 2 rails to connect predecoder and decoder stages."""
# This is not needed for inputs <4 since they have no pre/decode stages. # This is not needed for inputs <4 since they have no pre/decode stages.
@ -387,8 +420,8 @@ class hierarchical_decoder(design.design):
self.rail_x_offsets = [] self.rail_x_offsets = []
for i in range(self.total_number_of_predecoder_outputs): for i in range(self.total_number_of_predecoder_outputs):
# The offsets go into the negative x direction # The offsets go into the negative x direction
# assuming the predecodes are placed at (self.routing_width,0) # assuming the predecodes are placed at (self.internal_routing_width,0)
x_offset = self.metal2_pitch * i x_offset = self.m2_pitch * i
self.rail_x_offsets.append(x_offset+0.5*self.m2_width) self.rail_x_offsets.append(x_offset+0.5*self.m2_width)
self.add_rect(layer="metal2", self.add_rect(layer="metal2",
offset=vector(x_offset,0), offset=vector(x_offset,0),
@ -406,7 +439,7 @@ class hierarchical_decoder(design.design):
index = pre_num * 4 + i index = pre_num * 4 + i
out_name = "out[{}]".format(i) out_name = "out[{}]".format(i)
pin = self.pre2x4_inst[pre_num].get_pin(out_name) pin = self.pre2x4_inst[pre_num].get_pin(out_name)
self.connect_rail_m3(index, pin) self.connect_predecode_rail_m3(index, pin)
for pre_num in range(self.no_of_pre3x8): for pre_num in range(self.no_of_pre3x8):
@ -414,7 +447,7 @@ class hierarchical_decoder(design.design):
index = pre_num * 8 + i + self.no_of_pre2x4 * 4 index = pre_num * 8 + i + self.no_of_pre2x4 * 4
out_name = "out[{}]".format(i) out_name = "out[{}]".format(i)
pin = self.pre3x8_inst[pre_num].get_pin(out_name) pin = self.pre3x8_inst[pre_num].get_pin(out_name)
self.connect_rail_m3(index, pin) self.connect_predecode_rail_m3(index, pin)
@ -430,17 +463,17 @@ class hierarchical_decoder(design.design):
if (self.num_inputs == 4 or self.num_inputs == 5): if (self.num_inputs == 4 or self.num_inputs == 5):
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
self.connect_rail(index_A, self.nand_inst[row_index].get_pin("A")) self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_rail(index_B, self.nand_inst[row_index].get_pin("B")) self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
row_index = row_index + 1 row_index = row_index + 1
elif (self.num_inputs > 5): elif (self.num_inputs > 5):
for index_A in self.predec_groups[0]: for index_A in self.predec_groups[0]:
for index_B in self.predec_groups[1]: for index_B in self.predec_groups[1]:
for index_C in self.predec_groups[2]: for index_C in self.predec_groups[2]:
self.connect_rail(index_A, self.nand_inst[row_index].get_pin("A")) self.connect_predecode_rail(index_A, self.nand_inst[row_index].get_pin("A"))
self.connect_rail(index_B, self.nand_inst[row_index].get_pin("B")) self.connect_predecode_rail(index_B, self.nand_inst[row_index].get_pin("B"))
self.connect_rail(index_C, self.nand_inst[row_index].get_pin("C")) self.connect_predecode_rail(index_C, self.nand_inst[row_index].get_pin("C"))
row_index = row_index + 1 row_index = row_index + 1
def route_vdd_gnd(self): def route_vdd_gnd(self):
@ -476,7 +509,7 @@ class hierarchical_decoder(design.design):
self.copy_layout_pin(pre, "gnd") self.copy_layout_pin(pre, "gnd")
def connect_rail(self, rail_index, pin): def connect_predecode_rail(self, rail_index, pin):
""" Connect the routing rail to the given metal1 pin """ """ Connect the routing rail to the given metal1 pin """
rail_pos = vector(self.rail_x_offsets[rail_index],pin.lc().y) rail_pos = vector(self.rail_x_offsets[rail_index],pin.lc().y)
self.add_path("metal1", [rail_pos, pin.lc()]) self.add_path("metal1", [rail_pos, pin.lc()])
@ -485,7 +518,7 @@ class hierarchical_decoder(design.design):
rotate=90) rotate=90)
def connect_rail_m3(self, rail_index, pin): def connect_predecode_rail_m3(self, rail_index, pin):
""" Connect the routing rail to the given metal1 pin """ """ Connect the routing rail to the given metal1 pin """
mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2) mid_point = vector(pin.cx(), pin.cy()+self.inv.height/2)
rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y) rail_pos = vector(self.rail_x_offsets[rail_index],mid_point.y)

View File

@ -50,10 +50,6 @@ class hierarchical_predecode(design.design):
debug.error("Invalid number of predecode inputs.",-1) debug.error("Invalid number of predecode inputs.",-1)
def setup_constraints(self): def setup_constraints(self):
# use a conservative douple spacing just to get rid of annoying via DRCs
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)
# The rail offsets are indexed by the label # The rail offsets are indexed by the label
self.rails = {} self.rails = {}

View File

@ -53,11 +53,6 @@ class replica_bitline(design.design):
# These aren't for instantiating, but we use them to get the dimensions # These aren't for instantiating, but we use them to get the dimensions
self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height) self.poly_contact_offset = vector(0.5*contact.poly.width,0.5*contact.poly.height)
# M1/M2 routing pitch is based on contacted pitch
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)
# Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough # Quadrant 1: Replica bitline and such are not rotated, but they must be placed far enough
# away from the delay chain/inverter with space for three M2 tracks # away from the delay chain/inverter with space for three M2 tracks
self.bitcell_offset = vector(0,self.replica_bitcell.height) self.bitcell_offset = vector(0,self.replica_bitcell.height)

View File

@ -57,7 +57,6 @@ class single_level_column_mux_array(design.design):
def setup_layout_constants(self): def setup_layout_constants(self):
self.column_addr_size = num_of_inputs = int(self.words_per_row / 2) self.column_addr_size = num_of_inputs = int(self.words_per_row / 2)
self.width = self.columns * self.mux.width self.width = self.columns * self.mux.width
self.m1_pitch = contact.m1m2.width + max(drc["metal1_to_metal1"],drc["metal2_to_metal2"])
# one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br # one set of metal1 routes for select signals and a pair to interconnect the mux outputs bl/br
# one extra route pitch is to space from the sense amp # one extra route pitch is to space from the sense amp
self.route_height = (self.words_per_row + 3)*self.m1_pitch self.route_height = (self.words_per_row + 3)*self.m1_pitch

View File

@ -1,3 +0,0 @@
#!/bin/bash
python tests/regress.py -t freepdk45
python tests/regress.py -t scn3me_subm

View File

@ -1,19 +1,17 @@
import sys import sys
from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
import contact
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
import datetime import datetime
import getpass import getpass
import debug
import design
from sram_1bank import sram_1bank
from sram_2bank import sram_2bank
from sram_4bank import sram_4bank
from math import log,sqrt,ceil
from vector import vector from vector import vector
from globals import OPTS, print_time from globals import OPTS, print_time
class sram(design.design): class sram(sram_1bank,sram_2bank,sram_4bank):
""" """
Dynamically generated SRAM by connecting banks to control logic. The Dynamically generated SRAM by connecting banks to control logic. The
number of banks should be 1 , 2 or 4 number of banks should be 1 , 2 or 4
@ -35,8 +33,8 @@ class sram(design.design):
# reset the static duplicate name checker for unit tests # reset the static duplicate name checker for unit tests
# in case we create more than one SRAM # in case we create more than one SRAM
import design from design import design
design.design.name_map=[] design.name_map=[]
self.word_size = word_size self.word_size = word_size
self.num_words = num_words self.num_words = num_words
@ -46,18 +44,20 @@ class sram(design.design):
self.num_words)) self.num_words))
start_time = datetime.datetime.now() start_time = datetime.datetime.now()
design.design.__init__(self, name) self.compute_sizes()
# M1/M2 routing pitch is based on contacted pitch of the biggest layer
self.m1_pitch = max(contact.m1m2.width,contact.m1m2.height) + max(self.m1_space,self.m2_space)
self.m2_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
self.m3_pitch = max(contact.m2m3.width,contact.m2m3.height) + max(self.m2_space,self.m3_space)
if self.num_banks == 1:
sram_1bank.__init__(self,name)
elif self.num_banks == 2:
sram_2bank.__init__(self,name)
elif self.num_banks == 4:
sram_4bank.__init__(self,name)
else:
debug.error("Invalid number of banks.",-1)
self.control_size = 6 self.control_size = 6
self.bank_to_bus_distance = 5*self.m3_width self.bank_to_bus_distance = 5*self.m3_width
self.compute_sizes()
self.create_modules() self.create_modules()
self.add_pins() self.add_pins()
self.create_layout() self.create_layout()
@ -109,8 +109,10 @@ class sram(design.design):
debug.info(1,"Words per row: {}".format(self.words_per_row)) debug.info(1,"Words per row: {}".format(self.words_per_row))
def estimate_words_per_row(self,tentative_num_cols, word_size): def estimate_words_per_row(self,tentative_num_cols, word_size):
"""This provides a heuristic rounded estimate for the number of words """
per row.""" This provides a heuristic rounded estimate for the number of words
per row.
"""
if tentative_num_cols < 1.5*word_size: if tentative_num_cols < 1.5*word_size:
return 1 return 1
@ -120,7 +122,8 @@ class sram(design.design):
return 2 return 2
def amend_words_per_row(self,tentative_num_rows, words_per_row): def amend_words_per_row(self,tentative_num_rows, words_per_row):
"""This picks the number of words per row more accurately by limiting """
This picks the number of words per row more accurately by limiting
it to a minimum and maximum. it to a minimum and maximum.
""" """
# Recompute the words per row given a hard max # Recompute the words per row given a hard max
@ -138,7 +141,7 @@ class sram(design.design):
""" Add pins for entire SRAM. """ """ Add pins for entire SRAM. """
for i in range(self.word_size): for i in range(self.word_size):
self.add_pin("DATA[{0}]".format(i),"INOUT") self.add_pin("DIN[{0}]".format(i),"INPUT")
for i in range(self.addr_size): for i in range(self.addr_size):
self.add_pin("ADDR[{0}]".format(i),"INPUT") self.add_pin("ADDR[{0}]".format(i),"INPUT")
@ -147,162 +150,18 @@ class sram(design.design):
self.control_logic_outputs=self.control_logic.get_outputs() self.control_logic_outputs=self.control_logic.get_outputs()
self.add_pin_list(self.control_logic_inputs,"INPUT") self.add_pin_list(self.control_logic_inputs,"INPUT")
for i in range(self.word_size):
self.add_pin("DOUT[{0}]".format(i),"OUTPUT")
self.add_pin("vdd","POWER") self.add_pin("vdd","POWER")
self.add_pin("gnd","GROUND") self.add_pin("gnd","GROUND")
def create_layout(self): def create_layout(self):
""" Layout creation """ """ Layout creation """
self.add_modules()
if self.num_banks == 1: self.route()
self.add_single_bank_modules()
self.add_single_bank_pins()
self.route_single_bank()
elif self.num_banks == 2:
self.add_two_bank_modules()
self.route_two_banks()
elif self.num_banks == 4:
self.add_four_bank_modules()
self.route_four_banks()
else:
debug.error("Invalid number of banks.",-1)
def add_four_bank_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_four_banks()
self.compute_four_bank_offsets()
self.add_busses()
self.add_four_bank_logic()
self.width = self.bank_inst[1].ur().x
self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy())
def add_two_bank_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_two_banks()
self.compute_two_bank_offsets()
self.add_busses()
self.add_two_bank_logic()
self.width = self.bank_inst[1].ur().x
self.height = self.control_logic_inst.uy()
def add_single_bank_modules(self):
"""
This adds the moduels for a single bank SRAM with control
logic.
"""
# No orientation or offset
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
# are not recomputed using instance placement. So, place the control logic such that it aligns
# with the top of the SRAM.
control_pos = vector(-self.control_logic.width - self.m3_pitch,
3*self.supply_rail_width)
self.add_control_logic(position=control_pos)
# Leave room for the control routes to the left of the flops
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
control_pos.y + self.control_logic.height + self.m1_pitch)
self.add_addr_dff(addr_pos)
# two supply rails are already included in the bank, so just 2 here.
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height
def route_shared_banks(self):
""" Route the shared signals for two and four bank configurations. """
# create the input control pins
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# connect the control logic to the control bus
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="metal2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_via_center(("metal1","via1","metal2"),cross_pos)
# connect the bank select signals to the vertical bus
for i in range(self.num_banks):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("metal3",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
def route_four_banks(self):
""" Route all of the signals for the four bank SRAM. """
self.route_shared_banks()
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).bc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
for i in [2,3]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# route msb address bits
# route 2:4 decoder
self.route_double_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n in self.msb_bank_sel_addr: continue
for bank_id in [0,2]:
pin0_pos = self.bank_inst[bank_id].get_pin(n).rc()
pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])
def compute_bus_sizes(self): def compute_bus_sizes(self):
""" Compute the independent bus widths shared between two and four bank SRAMs """ """ Compute the independent bus widths shared between two and four bank SRAMs """
@ -325,417 +184,70 @@ class sram(design.design):
self.supply_bus_width = self.data_bus_width self.supply_bus_width = self.data_bus_width
# Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really) # Sanity check to ensure we can fit the control logic above a single bank (0.9 is a hack really)
debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width, "Bank is too small compared to control logic.") debug.check(self.bank.width + self.vertical_bus_width > 0.9*self.control_logic.width,
"Bank is too small compared to control logic.")
def compute_four_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
# The main difference is that the four bank SRAM has the data bus in the middle of the four banks
# as opposed to the top of the banks.
# In 4 bank SRAM, the height is determined by the bank decoder and address flop
self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \
+ self.supply_bus_height + self.msb_decoder.height + self.msb_address.width
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height + self.bank.height + 2*self.bank_to_bus_distance)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y + self.supply_bus_height + 2*self.m1_pitch + self.msb_address.width)
# Decoder goes above the MSB address flops, and is flipped in Y
# separate the two by a bank to bus distance for nwell rules, just in case
self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance)
def compute_two_bank_offsets(self):
""" Compute the overall offsets for a two bank SRAM """
# In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address
self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y+self.supply_bus_height + 2*self.m1_pitch + self.msb_address.width)
def add_busses(self): def add_busses(self):
""" Add the horizontal and vertical busses """ """ Add the horizontal and vertical busses """
# Vertical bus # Vertical bus
# The order of the control signals on the control bus: # The order of the control signals on the control bus:
self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"] self.control_bus_names = ["clk_buf", "tri_en_bar", "tri_en", "clk_buf_bar", "w_en", "s_en"]
self.vert_control_bus_positions = self.create_bus(layer="metal2", self.vert_control_bus_positions = self.create_vertical_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.vertical_bus_offset, offset=self.vertical_bus_offset,
names=self.control_bus_names, names=self.control_bus_names,
length=self.vertical_bus_height, length=self.vertical_bus_height)
vertical=True)
self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)] self.addr_bus_names=["A[{}]".format(i) for i in range(self.addr_size)]
self.vert_control_bus_positions.update(self.create_bus(layer="metal2", self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.addr_bus_offset, offset=self.addr_bus_offset,
names=self.addr_bus_names, names=self.addr_bus_names,
length=self.addr_bus_height, length=self.addr_bus_height))
vertical=True,
make_pins=True))
self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)] self.bank_sel_bus_names = ["bank_sel[{}]".format(i) for i in range(self.num_banks)]
self.vert_control_bus_positions.update(self.create_bus(layer="metal2", self.vert_control_bus_positions.update(self.create_vertical_pin_bus(layer="metal2",
pitch=self.m2_pitch, pitch=self.m2_pitch,
offset=self.bank_sel_bus_offset, offset=self.bank_sel_bus_offset,
names=self.bank_sel_bus_names, names=self.bank_sel_bus_names,
length=self.vertical_bus_height, length=self.vertical_bus_height))
vertical=True,
make_pins=True))
# Horizontal data bus # Horizontal data bus
self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)] self.data_bus_names = ["DATA[{}]".format(i) for i in range(self.word_size)]
self.data_bus_positions = self.create_bus(layer="metal3", self.data_bus_positions = self.create_horizontal_pin_bus(layer="metal3",
pitch=self.m3_pitch, pitch=self.m3_pitch,
offset=self.data_bus_offset, offset=self.data_bus_offset,
names=self.data_bus_names, names=self.data_bus_names,
length=self.data_bus_width, length=self.data_bus_width)
vertical=False,
make_pins=True)
# Horizontal control logic bus # Horizontal control logic bus
# vdd/gnd in bus go along whole SRAM # vdd/gnd in bus go along whole SRAM
# FIXME: Fatten these wires? # FIXME: Fatten these wires?
self.horz_control_bus_positions = self.create_bus(layer="metal1", self.horz_control_bus_positions = self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch, pitch=self.m1_pitch,
offset=self.supply_bus_offset, offset=self.supply_bus_offset,
names=["vdd"], names=["vdd"],
length=self.supply_bus_width, length=self.supply_bus_width)
vertical=False)
# The gnd rail must not be the entire width since we protrude the right-most vdd rail up for # The gnd rail must not be the entire width since we protrude the right-most vdd rail up for
# the decoder in 4-bank SRAMs # the decoder in 4-bank SRAMs
self.horz_control_bus_positions.update(self.create_bus(layer="metal1", self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
pitch=self.m1_pitch, pitch=self.m1_pitch,
offset=self.supply_bus_offset+vector(0,self.m1_pitch), offset=self.supply_bus_offset+vector(0,self.m1_pitch),
names=["gnd"], names=["gnd"],
length=self.supply_bus_width, length=self.supply_bus_width))
vertical=False)) self.horz_control_bus_positions.update(self.create_horizontal_bus(layer="metal1",
self.horz_control_bus_positions.update(self.create_bus(layer="metal1", pitch=self.m1_pitch,
pitch=self.m1_pitch, offset=self.control_bus_offset,
offset=self.control_bus_offset, names=self.control_bus_names,
names=self.control_bus_names, length=self.control_bus_width))
length=self.control_bus_width,
vertical=False))
def add_two_bank_logic(self):
""" Add the control and MSB logic """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = "ADDR[{}]".format(self.addr_size-1)
self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"])
def add_four_bank_logic(self):
""" Add the control and MSB decode/bank select logic for four banks """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)]
temp = list(self.msb_bank_sel_addr)
temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]])
temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp)
self.msb_decoder_inst = self.add_inst(name="msb_decoder",
mod=self.msb_decoder,
offset=self.msb_decoder_position,
mirror="MY")
temp = ["msb[{}]".format(i) for i in range(2)]
temp.extend(["bank_sel[{}]".format(i) for i in range(4)])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def route_two_banks(self):
""" Route all of the signals for the two bank SRAM. """
self.route_shared_banks()
# connect the horizontal control bus to the vertical bus
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_single_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n == self.msb_bank_sel_addr: continue
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
def route_double_msb_address(self):
""" Route two MSB address bits and the bank decoder for 4-bank SRAM """
# connect the MSB flops to the address input bus
for i in [0,1]:
msb_pins = self.msb_address_inst.get_pins("din[{}]".format(i))
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
# Connect bank decoder outputs to the bank select vertical bus wires
for i in range(self.num_banks):
msb_pin = self.msb_decoder_inst.get_pin("out[{}]".format(i))
msb_pin_pos = msb_pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y)
self.add_path("metal1",[msb_pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect MSB flop outputs to the bank decoder inputs
msb_pin = self.msb_address_inst.get_pin("dout[0]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[0]")
in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom
out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
msb_pin = self.msb_address_inst.get_pin("dout[1]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[1]")
in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up
out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
self.route_double_msb_address_supplies()
def route_double_msb_address_supplies(self):
""" Route the vdd/gnd bits of the 2-bit bank decoder. """
# Route the right-most vdd/gnd of the right upper bank to the top of the decoder
vdd_pins = self.bank_inst[1].get_pins("vdd")
left_bank_vdd_pin = None
right_bank_vdd_pin = None
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal2":
continue
if left_bank_vdd_pin == None or vdd_pin.lx()<left_bank_vdd_pin.lx():
left_bank_vdd_pin = vdd_pin
if right_bank_vdd_pin == None or vdd_pin.lx()>right_bank_vdd_pin.lx():
right_bank_vdd_pin = vdd_pin
# Route to top
self.add_rect(layer="metal2",
offset=vdd_pin.ul(),
height=self.height-vdd_pin.uy(),
width=vdd_pin.width())
gnd_pins = self.bank_inst[1].get_pins("gnd")
left_bank_gnd_pin = None
right_bank_gnd_pin = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
if left_bank_gnd_pin == None or gnd_pin.lx()<left_bank_gnd_pin.lx():
left_bank_gnd_pin = gnd_pin
if right_bank_gnd_pin == None or gnd_pin.lx()>right_bank_gnd_pin.lx():
right_bank_gnd_pin = gnd_pin
# Route to top
self.add_rect(layer="metal2",
offset=gnd_pin.ul(),
height=self.height-gnd_pin.uy(),
width=gnd_pin.width())
# Connect bank decoder vdd/gnd supplies using the previous bank pins
vdd_pins = self.msb_decoder_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy())
rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
gnd_pins = self.msb_decoder_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
# vdd pins go down to the rail
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=down_pos,
rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos)
# gnd pins go right to the rail
gnd_pins = self.msb_address_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,gnd_pin.lc()])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=gnd_pin.lc(),
rotate=90)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1": continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(("metal1","via1","metal2"),down_pos,rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("metal2",[gnd_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
def route_vdd_gnd(self): def route_vdd_gnd(self):
""" Propagate all vdd/gnd pins up to this level for all modules """ """ Propagate all vdd/gnd pins up to this level for all modules """
@ -768,7 +280,7 @@ class sram(design.design):
def create_multi_bank_modules(self): def create_multi_bank_modules(self):
""" Create the multibank address flops and bank decoder """ """ Create the multibank address flops and bank decoder """
from dff_buf_array import dff_buf_array
self.msb_address = dff_buf_array(name="msb_address", self.msb_address = dff_buf_array(name="msb_address",
rows=1, rows=1,
columns=self.num_banks/2) columns=self.num_banks/2)
@ -781,16 +293,19 @@ class sram(design.design):
def create_modules(self): def create_modules(self):
""" Create all the modules that will be used """ """ Create all the modules that will be used """
from control_logic import control_logic
# Create the control logic module # Create the control logic module
self.control_logic = self.mod_control_logic(num_rows=self.num_rows) self.control_logic = self.mod_control_logic(num_rows=self.num_rows)
self.add_mod(self.control_logic) self.add_mod(self.control_logic)
# Create the address and control flops (but not the clk) # Create the address and control flops (but not the clk)
dff_size = self.addr_size dff_size = self.addr_size
from dff_array import dff_array
self.addr_dff = dff_array(name="dff_array", rows=dff_size, columns=1) self.addr_dff = dff_array(name="dff_array", rows=dff_size, columns=1)
self.add_mod(self.addr_dff) self.add_mod(self.addr_dff)
# Create the bank module (up to four are instantiated) # Create the bank module (up to four are instantiated)
from bank import bank
self.bank = bank(word_size=self.word_size, self.bank = bank(word_size=self.word_size,
num_words=self.num_words_per_bank, num_words=self.num_words_per_bank,
words_per_row=self.words_per_row, words_per_row=self.words_per_row,
@ -854,44 +369,6 @@ class sram(design.design):
return bank_inst return bank_inst
# FIXME: This should be in geometry.py or it's own class since it is
# reusable
def create_bus(self, layer, pitch, offset, names, length, vertical=False, make_pins=False):
""" Create a horizontal or vertical bus. It can be either just rectangles, or actual
layout pins. It returns an map of line center line positions indexed by name. """
# half minwidth so we can return the center line offsets
half_minwidth = 0.5*drc["minwidth_{}".format(layer)]
line_positions = {}
if vertical:
for i in range(len(names)):
line_offset = offset + vector(i*pitch,0)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
height=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
height=length)
line_positions[names[i]]=line_offset+vector(half_minwidth,0)
else:
for i in range(len(names)):
line_offset = offset + vector(0,i*pitch + half_minwidth)
if make_pins:
self.add_layout_pin(text=names[i],
layer=layer,
offset=line_offset,
width=length)
else:
self.add_rect(layer=layer,
offset=line_offset,
width=length)
line_positions[names[i]]=line_offset+vector(0,half_minwidth)
return line_positions
def add_addr_dff(self, position): def add_addr_dff(self, position):
@ -939,51 +416,9 @@ class sram(design.design):
layer="metal2", layer="metal2",
offset=self.vert_control_bus_positions[n]) offset=self.vert_control_bus_positions[n])
def add_single_bank_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
for i in range(self.word_size):
self.copy_layout_pin(self.bank_inst, "DOUT[{}]".format(i))
for i in range(self.addr_size):
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
def add_two_banks(self):
# Placement of bank 0 (left)
bank_position_0 = vector(self.bank.width,
self.bank.height)
self.bank_inst=[self.add_bank(0, bank_position_0, -1, -1)]
# Placement of bank 1 (right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, -1, 1))
def add_four_banks(self):
# Placement of bank 0 (upper left)
bank_position_0 = vector(self.bank.width,
self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance)
self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)]
# Placement of bank 1 (upper right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1))
# Placement of bank 2 (bottom left)
y_off = self.bank.height
bank_position_2 = vector(bank_position_0.x, y_off)
self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1))
# Placement of bank 3 (bottom right)
bank_position_3 = vector(bank_position_1.x, bank_position_2.y)
self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1))
def connect_rail_from_left_m2m3(self, src_pin, dest_pin): def connect_rail_from_left_m2m3(self, src_pin, dest_pin):
""" Helper routine to connect an unrotated/mirrored oriented instance to the rails """ """ Helper routine to connect an unrotated/mirrored oriented instance to the rails """
@ -1000,49 +435,6 @@ class sram(design.design):
out_pos = vector(dest_pin.cx(), in_pos.y) out_pos = vector(dest_pin.cx(), in_pos.y)
self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)]) self.add_wire(("metal2","via1","metal1"),[in_pos, out_pos, out_pos - vector(0,self.m2_pitch)])
def route_single_bank(self):
""" Route a single bank SRAM """
# Route the outputs from the control logic module
for n in self.control_logic_outputs:
src_pin = self.control_logic_inst.get_pin(n)
dest_pin = self.bank_inst.get_pin(n)
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=src_pin.rc(),
rotate=90)
# Connect the output of the flops to the bank pins
for i in range(self.addr_size):
flop_name = "dout[{}]".format(i)
bank_name = "A[{}]".format(i)
flop_pin = self.addr_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center()
bank_pos = vector(bank_pin.cx(),flop_pos.y)
self.add_path("metal3",[flop_pos, bank_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=flop_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_pos,
rotate=90)
# Connect the control pins as inputs
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# Connect the clock between the flops and control module
flop_pin = self.addr_dff_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk_buf")
flop_pos = flop_pin.uc()
ctrl_pos = ctrl_pin.bc()
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
mid1_pos = vector(flop_pos.x, mid_ypos)
mid2_pos = vector(ctrl_pos.x, mid_ypos)
self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()])
def sp_write(self, sp_name): def sp_write(self, sp_name):

101
compiler/sram_1bank.py Normal file
View File

@ -0,0 +1,101 @@
import sys
from tech import drc, spice
import debug
from math import log,sqrt,ceil
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
from design import design
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
class sram_1bank(design):
"""
Procedures specific to a two bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def add_modules(self):
"""
This adds the moduels for a single bank SRAM with control
logic.
"""
# No orientation or offset
self.bank_inst = self.add_bank(0, [0, 0], 1, 1)
# 3/5/18 MRG: Cannot reference positions inside submodules because boundaries
# are not recomputed using instance placement. So, place the control logic such that it aligns
# with the top of the SRAM.
control_pos = vector(-self.control_logic.width - self.m3_pitch,
3*self.supply_rail_width)
self.add_control_logic(position=control_pos)
# Leave room for the control routes to the left of the flops
addr_pos = vector(self.control_logic_inst.lx() + 4*self.m2_pitch,
control_pos.y + self.control_logic.height + self.m1_pitch)
self.add_addr_dff(addr_pos)
# two supply rails are already included in the bank, so just 2 here.
self.width = self.bank.width + self.control_logic.width + 2*self.supply_rail_pitch
self.height = self.bank.height
def add_pins(self):
"""
Add the top-level pins for a single bank SRAM with control.
"""
for i in range(self.word_size):
self.copy_layout_pin(self.bank_inst, "DOUT[{}]".format(i))
for i in range(self.addr_size):
self.copy_layout_pin(self.addr_dff_inst, "din[{}]".format(i),"ADDR[{}]".format(i))
def route(self):
""" Route a single bank SRAM """
# Route the outputs from the control logic module
for n in self.control_logic_outputs:
src_pin = self.control_logic_inst.get_pin(n)
dest_pin = self.bank_inst.get_pin(n)
self.connect_rail_from_left_m2m3(src_pin, dest_pin)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=src_pin.rc(),
rotate=90)
# Connect the output of the flops to the bank pins
for i in range(self.addr_size):
flop_name = "dout[{}]".format(i)
bank_name = "A[{}]".format(i)
flop_pin = self.addr_dff_inst.get_pin(flop_name)
bank_pin = self.bank_inst.get_pin(bank_name)
flop_pos = flop_pin.center()
bank_pos = vector(bank_pin.cx(),flop_pos.y)
self.add_path("metal3",[flop_pos, bank_pos])
self.add_via_center(layers=("metal2","via2","metal3"),
offset=flop_pos,
rotate=90)
self.add_via_center(layers=("metal2","via2","metal3"),
offset=bank_pos,
rotate=90)
# Connect the control pins as inputs
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# Connect the clock between the flops and control module
flop_pin = self.addr_dff_inst.get_pin("clk")
ctrl_pin = self.control_logic_inst.get_pin("clk_buf")
flop_pos = flop_pin.uc()
ctrl_pos = ctrl_pin.bc()
mid_ypos = 0.5*(ctrl_pos.y+flop_pos.y)
mid1_pos = vector(flop_pos.x, mid_ypos)
mid2_pos = vector(ctrl_pos.x, mid_ypos)
self.add_wire(("metal1","via1","metal2"),[flop_pin.uc(), mid1_pos, mid2_pos, ctrl_pin.bc()])

216
compiler/sram_2bank.py Normal file
View File

@ -0,0 +1,216 @@
import sys
from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
import contact
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
class sram_2bank(design.design):
"""
Procedures specific to a two bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def compute_bank_offsets(self):
""" Compute the overall offsets for a two bank SRAM """
# In 2 bank SRAM, the height is determined by the control bus which is higher than the msb address
self.vertical_bus_height = self.bank.height + 2*self.bank_to_bus_distance + self.data_bus_height + self.control_bus_height
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y + self.supply_bus_height \
+ 2*self.m1_pitch + self.msb_address.width)
def add_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_banks()
self.compute_bank_offsets()
self.add_busses()
self.add_logic()
self.width = self.bank_inst[1].ur().x
self.height = self.control_logic_inst.uy()
def add_banks(self):
# Placement of bank 0 (left)
bank_position_0 = vector(self.bank.width,
self.bank.height)
self.bank_inst=[self.add_bank(0, bank_position_0, -1, -1)]
# Placement of bank 1 (right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, -1, 1))
def add_logic(self):
""" Add the control and MSB logic """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = "ADDR[{}]".format(self.addr_size-1)
self.connect_inst([self.msb_bank_sel_addr,"bank_sel[1]","bank_sel[0]","clk_buf", "vdd", "gnd"])
def route_shared_banks(self):
""" Route the shared signals for two and four bank configurations. """
# create the input control pins
for n in self.control_logic_inputs + ["clk"]:
self.copy_layout_pin(self.control_logic_inst, n)
# connect the control logic to the control bus
for n in self.control_logic_outputs + ["vdd", "gnd"]:
pins = self.control_logic_inst.get_pins(n)
for pin in pins:
if pin.layer=="metal2":
pin_pos = pin.bc()
break
rail_pos = vector(pin_pos.x,self.horz_control_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect the control logic cross bar
for n in self.control_logic_outputs:
cross_pos = vector(self.vert_control_bus_positions[n].x,self.horz_control_bus_positions[n].y)
self.add_via_center(("metal1","via1","metal2"),cross_pos)
# connect the bank select signals to the vertical bus
for i in range(self.num_banks):
pin = self.bank_inst[i].get_pin("bank_sel")
pin_pos = pin.rc() if i==0 else pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,pin_pos.y)
self.add_path("metal3",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
def route_single_msb_address(self):
""" Route one MSB address bit for 2-bank SRAM """
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1": continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(("metal1","via1","metal2"),down_pos,rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
gnd_pins = self.msb_address_inst.get_pins("gnd")
# Only add the ground connection to the lowest metal2 rail in the flop array
# FIXME: SCMOS doesn't have a vertical rail in the cell, or we could use those
lowest_y = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2": continue
if lowest_y==None or gnd_pin.by()<lowest_y:
lowest_y=gnd_pin.by()
gnd_pos = gnd_pin.ur()
rail_pos = vector(gnd_pos.x,self.horz_control_bus_positions["gnd"].y)
self.add_path("metal2",[gnd_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect the MSB flop to the address input bus
msb_pins = self.msb_address_inst.get_pins("din[0]")
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect the output bar to select 0
msb_out_pin = self.msb_address_inst.get_pin("dout_bar[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_up_pos = out_extend_right_pos + vector(0,self.m2_width)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[0]"].x,out_extend_up_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_up_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_up_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect the output to select 1
msb_out_pin = self.msb_address_inst.get_pin("dout[0]")
msb_out_pos = msb_out_pin.rc()
out_extend_right_pos = msb_out_pos + vector(2*self.m2_pitch,0)
out_extend_down_pos = out_extend_right_pos - vector(0,2*self.m1_pitch)
rail_pos = vector(self.vert_control_bus_positions["bank_sel[1]"].x,out_extend_down_pos.y)
self.add_path("metal2",[msb_out_pos,out_extend_right_pos,out_extend_down_pos])
self.add_wire(("metal3","via2","metal2"),[out_extend_right_pos,out_extend_down_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
def route(self):
""" Route all of the signals for the two bank SRAM. """
self.route_shared_banks()
# connect the horizontal control bus to the vertical bus
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_single_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n == self.msb_bank_sel_addr: continue
pin0_pos = self.bank_inst[0].get_pin(n).rc()
pin1_pos = self.bank_inst[1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)

314
compiler/sram_4bank.py Normal file
View File

@ -0,0 +1,314 @@
import sys
from tech import drc, spice
import debug
import design
from math import log,sqrt,ceil
import contact
from bank import bank
from dff_buf_array import dff_buf_array
from dff_array import dff_array
import datetime
import getpass
from vector import vector
from globals import OPTS, print_time
class sram_4bank(design.design):
"""
Procedures specific to a four bank SRAM.
"""
def __init__(self, name):
design.__init__(self, name)
def compute_bank_offsets(self):
""" Compute the overall offsets for a four bank SRAM """
# The main difference is that the four bank SRAM has the data bus in the middle of the four banks
# as opposed to the top of the banks.
# In 4 bank SRAM, the height is determined by the bank decoder and address flop
self.vertical_bus_height = 2*self.bank.height + 4*self.bank_to_bus_distance + self.data_bus_height \
+ self.supply_bus_height + self.msb_decoder.height + self.msb_address.width
# The address bus extends down through the power rails, but control and bank_sel bus don't
self.addr_bus_height = self.vertical_bus_height
self.vertical_bus_offset = vector(self.bank.width + self.bank_to_bus_distance, 0)
self.data_bus_offset = vector(0, self.bank.height + self.bank_to_bus_distance)
self.supply_bus_offset = vector(0, self.data_bus_offset.y + self.data_bus_height \
+ self.bank.height + 2*self.bank_to_bus_distance)
self.control_bus_offset = vector(0, self.supply_bus_offset.y + self.supply_bus_height)
self.bank_sel_bus_offset = self.vertical_bus_offset + vector(self.m2_pitch*self.control_size,0)
self.addr_bus_offset = self.bank_sel_bus_offset.scale(1,0) + vector(self.m2_pitch*self.num_banks,0)
# Control is placed at the top above the control bus and everything
self.control_logic_position = vector(0, self.control_bus_offset.y + self.control_bus_height + self.m1_pitch)
# Bank select flops get put to the right of control logic above bank1 and the buses
# Leave a pitch to get the vdd rails up to M2
self.msb_address_position = vector(self.bank_inst[1].lx() + 3*self.supply_rail_pitch,
self.supply_bus_offset.y + self.supply_bus_height \
+ 2*self.m1_pitch + self.msb_address.width)
# Decoder goes above the MSB address flops, and is flipped in Y
# separate the two by a bank to bus distance for nwell rules, just in case
self.msb_decoder_position = self.msb_address_position + vector(self.msb_decoder.width, self.bank_to_bus_distance)
def add_modules(self):
""" Adds the modules and the buses to the top level """
self.compute_bus_sizes()
self.add_banks()
self.compute_bank_offsets()
self.add_busses()
self.add_logic()
self.width = self.bank_inst[1].ur().x
self.height = max(self.control_logic_inst.uy(),self.msb_decoder_inst.uy())
def add_banks(self):
# Placement of bank 0 (upper left)
bank_position_0 = vector(self.bank.width,
self.bank.height + self.data_bus_height + 2*self.bank_to_bus_distance)
self.bank_inst=[self.add_bank(0, bank_position_0, 1, -1)]
# Placement of bank 1 (upper right)
x_off = self.bank.width + self.vertical_bus_width + 2*self.bank_to_bus_distance
bank_position_1 = vector(x_off, bank_position_0.y)
self.bank_inst.append(self.add_bank(1, bank_position_1, 1, 1))
# Placement of bank 2 (bottom left)
y_off = self.bank.height
bank_position_2 = vector(bank_position_0.x, y_off)
self.bank_inst.append(self.add_bank(2, bank_position_2, -1, -1))
# Placement of bank 3 (bottom right)
bank_position_3 = vector(bank_position_1.x, bank_position_2.y)
self.bank_inst.append(self.add_bank(3, bank_position_3, -1, 1))
def add_logic(self):
""" Add the control and MSB decode/bank select logic for four banks """
self.add_control_logic(position=self.control_logic_position)
self.msb_address_inst = self.add_inst(name="msb_address",
mod=self.msb_address,
offset=self.msb_address_position,
rotate=270)
self.msb_bank_sel_addr = ["ADDR[{}]".format(i) for i in range(self.addr_size-2,self.addr_size,1)]
temp = list(self.msb_bank_sel_addr)
temp.extend(["msb{0}[{1}]".format(j,i) for i in range(2) for j in ["","_bar"]])
temp.extend(["clk_buf", "vdd", "gnd"])
self.connect_inst(temp)
self.msb_decoder_inst = self.add_inst(name="msb_decoder",
mod=self.msb_decoder,
offset=self.msb_decoder_position,
mirror="MY")
temp = ["msb[{}]".format(i) for i in range(2)]
temp.extend(["bank_sel[{}]".format(i) for i in range(4)])
temp.extend(["vdd", "gnd"])
self.connect_inst(temp)
def route_double_msb_address(self):
""" Route two MSB address bits and the bank decoder for 4-bank SRAM """
# connect the MSB flops to the address input bus
for i in [0,1]:
msb_pins = self.msb_address_inst.get_pins("din[{}]".format(i))
for msb_pin in msb_pins:
if msb_pin.layer == "metal3":
msb_pin_pos = msb_pin.lc()
break
rail_pos = vector(self.vert_control_bus_positions[self.msb_bank_sel_addr[i]].x,msb_pin_pos.y)
self.add_path("metal3",[msb_pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# Connect clk
clk_pin = self.msb_address_inst.get_pin("clk")
clk_pos = clk_pin.bc()
rail_pos = self.horz_control_bus_positions["clk_buf"]
bend_pos = vector(clk_pos.x,self.horz_control_bus_positions["clk_buf"].y)
self.add_path("metal1",[clk_pos,bend_pos,rail_pos])
# Connect bank decoder outputs to the bank select vertical bus wires
for i in range(self.num_banks):
msb_pin = self.msb_decoder_inst.get_pin("out[{}]".format(i))
msb_pin_pos = msb_pin.lc()
rail_pos = vector(self.vert_control_bus_positions["bank_sel[{}]".format(i)].x,msb_pin_pos.y)
self.add_path("metal1",[msb_pin_pos,rail_pos])
self.add_via_center(("metal1","via1","metal2"),rail_pos)
# connect MSB flop outputs to the bank decoder inputs
msb_pin = self.msb_address_inst.get_pin("dout[0]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[0]")
in_pos = in_pin.bc() + vector(0,1*self.m2_pitch,) # pin is up from bottom
out_pos = msb_pin_pos + vector(1*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
msb_pin = self.msb_address_inst.get_pin("dout[1]")
msb_pin_pos = msb_pin.rc()
in_pin = self.msb_decoder_inst.get_pin("in[1]")
in_pos = in_pin.bc() + vector(0,self.bitcell.height+self.m2_pitch) # route the next row up
out_pos = msb_pin_pos + vector(2*self.m2_pitch,0) # route out to the right
up_pos = vector(out_pos.x,in_pos.y) # and route up to the decoer
self.add_wire(("metal1","via1","metal2"),[msb_pin_pos,out_pos,up_pos,in_pos])
self.add_via_center(("metal1","via1","metal2"),in_pos)
self.add_via_center(("metal1","via1","metal2"),msb_pin_pos,rotate=90)
self.route_double_msb_address_supplies()
def route_double_msb_address_supplies(self):
""" Route the vdd/gnd bits of the 2-bit bank decoder. """
# Route the right-most vdd/gnd of the right upper bank to the top of the decoder
vdd_pins = self.bank_inst[1].get_pins("vdd")
left_bank_vdd_pin = None
right_bank_vdd_pin = None
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal2":
continue
if left_bank_vdd_pin == None or vdd_pin.lx()<left_bank_vdd_pin.lx():
left_bank_vdd_pin = vdd_pin
if right_bank_vdd_pin == None or vdd_pin.lx()>right_bank_vdd_pin.lx():
right_bank_vdd_pin = vdd_pin
# Route to top
self.add_rect(layer="metal2",
offset=vdd_pin.ul(),
height=self.height-vdd_pin.uy(),
width=vdd_pin.width())
gnd_pins = self.bank_inst[1].get_pins("gnd")
left_bank_gnd_pin = None
right_bank_gnd_pin = None
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
if left_bank_gnd_pin == None or gnd_pin.lx()<left_bank_gnd_pin.lx():
left_bank_gnd_pin = gnd_pin
if right_bank_gnd_pin == None or gnd_pin.lx()>right_bank_gnd_pin.lx():
right_bank_gnd_pin = gnd_pin
# Route to top
self.add_rect(layer="metal2",
offset=gnd_pin.ul(),
height=self.height-gnd_pin.uy(),
width=gnd_pin.width())
# Connect bank decoder vdd/gnd supplies using the previous bank pins
vdd_pins = self.msb_decoder_inst.get_pins("vdd")
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_vdd_pin.cx(),vdd_pin.cy())
rail2_pos = vector(right_bank_vdd_pin.cx(),vdd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
gnd_pins = self.msb_decoder_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal1":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
rail2_pos = vector(right_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,rail2_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail2_pos,
rotate=90,
size=[1,3])
# connect the bank MSB flop supplies
vdd_pins = self.msb_address_inst.get_pins("vdd")
# vdd pins go down to the rail
for vdd_pin in vdd_pins:
if vdd_pin.layer != "metal1":
continue
vdd_pos = vdd_pin.bc()
down_pos = vdd_pos - vector(0,self.m1_pitch)
rail_pos = vector(vdd_pos.x,self.horz_control_bus_positions["vdd"].y)
self.add_path("metal1",[vdd_pos,down_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=down_pos,
rotate=90)
self.add_path("metal2",[down_pos,rail_pos])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail_pos)
# gnd pins go right to the rail
gnd_pins = self.msb_address_inst.get_pins("gnd")
for gnd_pin in gnd_pins:
if gnd_pin.layer != "metal2":
continue
rail1_pos = vector(left_bank_gnd_pin.cx(),gnd_pin.cy())
self.add_path("metal1",[rail1_pos,gnd_pin.lc()])
self.add_via_center(layers=("metal1","via1","metal2"),
offset=gnd_pin.lc(),
rotate=90)
self.add_via_center(layers=("metal1","via1","metal2"),
offset=rail1_pos,
rotate=90,
size=[1,3])
def route(self):
""" Route all of the signals for the four bank SRAM. """
self.route_shared_banks()
# connect the data output to the data bus
for n in self.data_bus_names:
for i in [0,1]:
pin_pos = self.bank_inst[i].get_pin(n).bc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
for i in [2,3]:
pin_pos = self.bank_inst[i].get_pin(n).uc()
rail_pos = vector(pin_pos.x,self.data_bus_positions[n].y)
self.add_path("metal2",[pin_pos,rail_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
# route msb address bits
# route 2:4 decoder
self.route_double_msb_address()
# connect the banks to the vertical address bus
# connect the banks to the vertical control bus
for n in self.addr_bus_names + self.control_bus_names:
# Skip these from the horizontal bus
if n in ["vdd", "gnd"]: continue
# This will be the bank select, so skip it
if n in self.msb_bank_sel_addr: continue
for bank_id in [0,2]:
pin0_pos = self.bank_inst[bank_id].get_pin(n).rc()
pin1_pos = self.bank_inst[bank_id+1].get_pin(n).lc()
rail_pos = vector(self.vert_control_bus_positions[n].x,pin0_pos.y)
self.add_path("metal3",[pin0_pos,pin1_pos])
self.add_via_center(("metal2","via2","metal3"),rail_pos)
self.route_bank_supply_rails(left_banks=[0,2], bottom_banks=[2,3])

View File

@ -13,7 +13,6 @@ class library_drc_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify
import verify import verify
(gds_dir, gds_files) = setup_files() (gds_dir, gds_files) = setup_files()
@ -31,11 +30,12 @@ class library_drc_test(openram_test):
self.assertEqual(drc_errors, 0) self.assertEqual(drc_errors, 0)
globals.end_openram() globals.end_openram()
def setup_files(): def setup_files():
gds_dir = OPTS.openram_tech + "/gds_lib" gds_dir = OPTS.openram_tech + "/gds_lib"
files = os.listdir(gds_dir) files = os.listdir(gds_dir)
nametest = re.compile("\.gds$", re.IGNORECASE) nametest = re.compile("\.gds$", re.IGNORECASE)
gds_files = filter(nametest.search, files) gds_files = list(filter(nametest.search, files))
return (gds_dir, gds_files) return (gds_dir, gds_files)
@ -45,3 +45,4 @@ if __name__ == "__main__":
del sys.argv[1:] del sys.argv[1:]
header(__file__, OPTS.tech_name) header(__file__, OPTS.tech_name)
unittest.main() unittest.main()

View File

@ -14,6 +14,7 @@ class library_lvs_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
import verify import verify
(gds_dir, sp_dir, allnames) = setup_files() (gds_dir, sp_dir, allnames) = setup_files()
lvs_errors = 0 lvs_errors = 0
debug.info(1, "Performing LVS on: " + ", ".join(allnames)) debug.info(1, "Performing LVS on: " + ", ".join(allnames))
@ -28,7 +29,7 @@ class library_lvs_test(openram_test):
lvs_errors += 1 lvs_errors += 1
debug.error("Missing SPICE file {}".format(gds_name)) debug.error("Missing SPICE file {}".format(gds_name))
lvs_errors += verify.run_lvs(f, gds_name, sp_name) lvs_errors += verify.run_lvs(f, gds_name, sp_name)
self.assertEqual(lvs_errors, 0)
# fail if the error count is not zero # fail if the error count is not zero
self.assertEqual(lvs_errors, 0) self.assertEqual(lvs_errors, 0)
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class contact_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import contact import contact
@ -43,7 +42,6 @@ class contact_test(openram_test):
c = contact.contact(layer_stack, (3, 3)) c = contact.contact(layer_stack, (3, 3))
self.local_drc_check(c) self.local_drc_check(c)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,8 +15,7 @@ class path_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import path import path
import tech import tech
import design import design
@ -84,8 +83,6 @@ class path_test(openram_test):
path.path(w, layer_stack, position_list) path.path(w, layer_stack, position_list)
self.local_drc_check(w) self.local_drc_check(w)
# return it back to it's normal state
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -26,7 +25,6 @@ class ptx_test(openram_test):
tx_type="nmos") tx_type="nmos")
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -26,7 +25,6 @@ class ptx_test(openram_test):
tx_type="pmos") tx_type="pmos")
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -28,7 +27,6 @@ class ptx_test(openram_test):
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -28,7 +27,6 @@ class ptx_test(openram_test):
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -28,7 +27,6 @@ class ptx_test(openram_test):
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class ptx_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ptx import ptx
import tech import tech
@ -28,7 +27,6 @@ class ptx_test(openram_test):
connect_poly=True) connect_poly=True)
self.local_drc_check(fet) self.local_drc_check(fet)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class wire_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import wire import wire
import tech import tech
@ -122,8 +121,6 @@ class wire_test(openram_test):
wire.wire(w, layer_stack, position_list) wire.wire(w, layer_stack, position_list)
self.local_drc_check(w) self.local_drc_check(w)
# return it back to it's normal state
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -22,7 +22,6 @@ class pbitcell_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pbitcell import pbitcell
import tech import tech
@ -67,9 +66,8 @@ class pbitcell_test(openram_test):
tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0) tx = pbitcell.pbitcell(num_readwrite=2,num_write=0,num_read=0)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
OPTS.bitcell = "bitcell"
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class pinv_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pinv import pinv
import tech import tech
@ -26,7 +25,6 @@ class pinv_test(openram_test):
tx = pinv.pinv(size=8) tx = pinv.pinv(size=8)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -17,7 +17,6 @@ class pinv_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pinv import pinv
import tech import tech
@ -26,7 +25,6 @@ class pinv_test(openram_test):
tx = pinv.pinv(size=1, beta=3) tx = pinv.pinv(size=1, beta=3)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -16,7 +16,6 @@ class pinv_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pinv import pinv
@ -26,7 +25,6 @@ class pinv_test(openram_test):
tx = pinv.pinv(size=1) tx = pinv.pinv(size=1)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class pinv_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pinv import pinv
import tech import tech
@ -26,7 +25,6 @@ class pinv_test(openram_test):
tx = pinv.pinv(size=2) tx = pinv.pinv(size=2)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -17,7 +17,6 @@ class pinvbuf_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pinvbuf import pinvbuf
@ -25,7 +24,6 @@ class pinvbuf_test(openram_test):
a = pinvbuf.pinvbuf(4,8) a = pinvbuf.pinvbuf(4,8)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -19,7 +19,6 @@ class pnand2_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pnand2 import pnand2
import tech import tech
@ -28,7 +27,6 @@ class pnand2_test(openram_test):
tx = pnand2.pnand2(size=1) tx = pnand2.pnand2(size=1)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -19,7 +19,6 @@ class pnand3_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pnand3 import pnand3
import tech import tech
@ -28,7 +27,6 @@ class pnand3_test(openram_test):
tx = pnand3.pnand3(size=1) tx = pnand3.pnand3(size=1)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -19,7 +19,6 @@ class pnor2_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import pnor2 import pnor2
import tech import tech
@ -28,7 +27,6 @@ class pnor2_test(openram_test):
tx = pnor2.pnor2(size=1) tx = pnor2.pnor2(size=1)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class precharge_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import precharge import precharge
import tech import tech
@ -26,7 +25,6 @@ class precharge_test(openram_test):
tx = precharge.precharge(name="precharge_driver", size=1) tx = precharge.precharge(name="precharge_driver", size=1)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -19,7 +19,6 @@ class single_level_column_mux_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import single_level_column_mux import single_level_column_mux
import tech import tech
@ -28,7 +27,6 @@ class single_level_column_mux_test(openram_test):
tx = single_level_column_mux.single_level_column_mux(tx_size=8) tx = single_level_column_mux.single_level_column_mux(tx_size=8)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -19,7 +19,6 @@ class array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import bitcell_array import bitcell_array
@ -27,7 +26,6 @@ class array_test(openram_test):
a = bitcell_array.bitcell_array(name="bitcell_array", cols=4, rows=4) a = bitcell_array.bitcell_array(name="bitcell_array", cols=4, rows=4)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -11,27 +11,26 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
#@unittest.skip("SKIPPING 05_array_multiport_test") #@unittest.skip("SKIPPING 05_pbitcell_array_test")
class pbitcell_array_test(openram_test):
class array_multiport_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import bitcell_array import bitcell_array
debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell")
OPTS.bitcell = "pbitcell" OPTS.bitcell = "pbitcell"
OPTS.rw_ports = 2 OPTS.rw_ports = 2
OPTS.r_ports = 2 OPTS.r_ports = 2
OPTS.w_ports = 2 OPTS.w_ports = 2
debug.info(2, "Testing 4x4 array for multiport bitcell, with read ports at the edge of the bit cell")
a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4)
self.local_check(a) self.local_check(a)
debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell")
OPTS.bitcell = "pbitcell"
OPTS.rw_ports = 2 OPTS.rw_ports = 2
OPTS.r_ports = 0 OPTS.r_ports = 0
OPTS.w_ports = 2 OPTS.w_ports = 2
@ -47,10 +46,12 @@ class array_multiport_test(openram_test):
debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell") debug.info(2, "Testing 4x4 array for multiport bitcell, with read/write ports at the edge of the bit cell")
a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4) a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4)
self.local_check(a) self.local_check(a)
a = bitcell_array.bitcell_array(name="pbitcell_array", cols=4, rows=4)
self.local_check(a)
OPTS.bitcell = "bitcell"
OPTS.check_lvsdrc = True OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
OPTS.bitcell = "bitcell"
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -17,7 +17,6 @@ class hierarchical_decoder_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import hierarchical_decoder import hierarchical_decoder
import tech import tech
@ -48,7 +47,6 @@ class hierarchical_decoder_test(openram_test):
a = hierarchical_decoder.hierarchical_decoder(rows=512) a = hierarchical_decoder.hierarchical_decoder(rows=512)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class hierarchical_predecode2x4_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import hierarchical_predecode2x4 as pre import hierarchical_predecode2x4 as pre
import tech import tech
@ -26,7 +25,6 @@ class hierarchical_predecode2x4_test(openram_test):
a = pre.hierarchical_predecode2x4() a = pre.hierarchical_predecode2x4()
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class hierarchical_predecode3x8_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import hierarchical_predecode3x8 as pre import hierarchical_predecode3x8 as pre
import tech import tech
@ -26,7 +25,6 @@ class hierarchical_predecode3x8_test(openram_test):
a = pre.hierarchical_predecode3x8() a = pre.hierarchical_predecode3x8()
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -16,7 +16,6 @@ class single_level_column_mux_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import single_level_column_mux_array import single_level_column_mux_array
@ -32,7 +31,6 @@ class single_level_column_mux_test(openram_test):
a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4) a = single_level_column_mux_array.single_level_column_mux_array(columns=32, word_size=4)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -17,7 +17,6 @@ class precharge_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import precharge_array import precharge_array
import tech import tech
@ -26,7 +25,6 @@ class precharge_test(openram_test):
pc = precharge_array.precharge_array(columns=3) pc = precharge_array.precharge_array(columns=3)
self.local_check(pc) self.local_check(pc)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()

View File

@ -19,7 +19,6 @@ class wordline_driver_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import wordline_driver import wordline_driver
import tech import tech
@ -28,7 +27,6 @@ class wordline_driver_test(openram_test):
tx = wordline_driver.wordline_driver(rows=8) tx = wordline_driver.wordline_driver(rows=8)
self.local_check(tx) self.local_check(tx)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class sense_amp_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import sense_amp_array import sense_amp_array
@ -30,7 +29,6 @@ class sense_amp_test(openram_test):
a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4) a = sense_amp_array.sense_amp_array(word_size=4, words_per_row=4)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class write_driver_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import write_driver_array import write_driver_array
@ -29,7 +28,6 @@ class write_driver_test(openram_test):
a = write_driver_array.write_driver_array(columns=16, word_size=8) a = write_driver_array.write_driver_array(columns=16, word_size=8)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import dff_array import dff_array
@ -33,7 +32,6 @@ class dff_array_test(openram_test):
a = dff_array.dff_array(rows=3, columns=1) a = dff_array.dff_array(rows=3, columns=1)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_buf_array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import dff_buf_array import dff_buf_array
@ -33,7 +32,6 @@ class dff_buf_array_test(openram_test):
a = dff_buf_array.dff_buf_array(rows=3, columns=1) a = dff_buf_array.dff_buf_array(rows=3, columns=1)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_buf_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import dff_buf import dff_buf
@ -25,7 +24,6 @@ class dff_buf_test(openram_test):
a = dff_buf.dff_buf(4, 8) a = dff_buf.dff_buf(4, 8)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_inv_array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import dff_inv_array import dff_inv_array
@ -33,7 +32,6 @@ class dff_inv_array_test(openram_test):
a = dff_inv_array.dff_inv_array(rows=3, columns=1) a = dff_inv_array.dff_inv_array(rows=3, columns=1)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_inv_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import dff_inv import dff_inv
@ -25,7 +24,6 @@ class dff_inv_test(openram_test):
a = dff_inv.dff_inv(4) a = dff_inv.dff_inv(4)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class dff_array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import ms_flop_array import ms_flop_array
@ -29,7 +28,6 @@ class dff_array_test(openram_test):
a = ms_flop_array.ms_flop_array(columns=16, word_size=8) a = ms_flop_array.ms_flop_array(columns=16, word_size=8)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class tri_gate_array_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import tri_gate_array import tri_gate_array
@ -29,7 +28,6 @@ class tri_gate_array_test(openram_test):
a = tri_gate_array.tri_gate_array(columns=16, word_size=8) a = tri_gate_array.tri_gate_array(columns=16, word_size=8)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class delay_chain_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import delay_chain import delay_chain
@ -25,7 +24,6 @@ class delay_chain_test(openram_test):
a = delay_chain.delay_chain(fanout_list=[4, 4, 4, 4]) a = delay_chain.delay_chain(fanout_list=[4, 4, 4, 4])
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class replica_bitline_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import replica_bitline import replica_bitline
@ -34,7 +33,6 @@ class replica_bitline_test(openram_test):
a = replica_bitline.replica_bitline(stages,fanout,rows) a = replica_bitline.replica_bitline(stages,fanout,rows)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class control_logic_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import control_logic import control_logic
import tech import tech
@ -26,7 +25,6 @@ class control_logic_test(openram_test):
a = control_logic.control_logic(num_rows=128) a = control_logic.control_logic(num_rows=128)
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -17,7 +17,6 @@ class bank_select_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import bank_select import bank_select
@ -25,7 +24,6 @@ class bank_select_test(openram_test):
a = bank_select.bank_select() a = bank_select.bank_select()
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class multi_bank_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import bank import bank
@ -37,7 +36,6 @@ class multi_bank_test(openram_test):
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi") a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=2, name="bank4_multi")
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class single_bank_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import bank import bank
@ -38,7 +37,6 @@ class single_bank_test(openram_test):
a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single") a = bank.bank(word_size=2, num_words=128, words_per_row=8, num_banks=1, name="bank4_single")
self.local_check(a) self.local_check(a)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -17,7 +17,6 @@ class sram_1bank_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import sram import sram
@ -37,7 +36,6 @@ class sram_1bank_test(openram_test):
# a = sram.sram(word_size=2, num_words=128, num_banks=1, name="sram4") # a = sram.sram(word_size=2, num_words=128, num_banks=1, name="sram4")
# self.local_check(a, final_verification=True) # self.local_check(a, final_verification=True)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -18,7 +18,6 @@ class sram_2bank_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import sram import sram
@ -38,7 +37,6 @@ class sram_2bank_test(openram_test):
# a = sram.sram(word_size=2, num_words=256 num_banks=2, name="sram4") # a = sram.sram(word_size=2, num_words=256 num_banks=2, name="sram4")
# self.local_check(a, final_verification=True) # self.local_check(a, final_verification=True)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -18,7 +18,6 @@ class sram_4bank_test(openram_test):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
global verify global verify
import verify import verify
OPTS.check_lvsdrc = False
import sram import sram
@ -38,7 +37,6 @@ class sram_4bank_test(openram_test):
# a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4") # a = sram.sram(word_size=2, num_words=256, num_banks=4, name="sram4")
# self.local_check(a, final_verification=True) # self.local_check(a, final_verification=True)
OPTS.check_lvsdrc = True
globals.end_openram() globals.end_openram()
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test

View File

@ -15,9 +15,9 @@ class timing_sram_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="hspice" OPTS.spice_name="hspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
# This is a hack to reload the characterizer __init__ with the spice version # This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload from importlib import reload
@ -35,7 +35,6 @@ class timing_sram_test(openram_test):
num_banks=OPTS.num_banks, num_banks=OPTS.num_banks,
name="sram1") name="sram1")
OPTS.check_lvsdrc = True
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
@ -85,12 +84,6 @@ class timing_sram_test(openram_test):
else: else:
self.isclose(data[k],golden_data[k],0.15) self.isclose(data[k],golden_data[k],0.15)
# reset these options
OPTS.check_lvsdrc = True
OPTS.analytical_delay = True
reload(characterizer)
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -15,7 +15,6 @@ class timing_setup_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="hspice" OPTS.spice_name="hspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
@ -27,7 +26,6 @@ class timing_setup_test(openram_test):
if not OPTS.spice_exe: if not OPTS.spice_exe:
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
import sram import sram
import tech import tech
slews = [tech.spice["rise_time"]*2] slews = [tech.spice["rise_time"]*2]
@ -59,9 +57,6 @@ class timing_setup_test(openram_test):
else: else:
self.isclose(data[k],golden_data[k],0.15) self.isclose(data[k],golden_data[k],0.15)
OPTS.check_lvsdrc = True
OPTS.analytical_delay = True
reload(characterizer)
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -15,7 +15,6 @@ class timing_sram_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="ngspice" OPTS.spice_name="ngspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
@ -84,12 +83,6 @@ class timing_sram_test(openram_test):
else: else:
self.isclose(data[k],golden_data[k],0.15) self.isclose(data[k],golden_data[k],0.15)
# reset these options
OPTS.check_lvsdrc = True
OPTS.spice_name="hspice"
OPTS.analytical_delay = True
reload(characterizer)
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

View File

@ -15,7 +15,6 @@ class timing_setup_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="ngspice" OPTS.spice_name="ngspice"
OPTS.analytical_delay = False OPTS.analytical_delay = False
@ -58,12 +57,7 @@ class timing_setup_test(openram_test):
else: else:
self.isclose(data[k],golden_data[k],0.15) self.isclose(data[k],golden_data[k],0.15)
# reset these options
OPTS.check_lvsdrc = True
OPTS.spice_name="hspice"
OPTS.analytical_delay = True
reload(characterizer) reload(characterizer)
globals.end_openram() globals.end_openram()
# instantiate a copdsay of the class to actually run the test # instantiate a copdsay of the class to actually run the test

19
compiler/tests/22_pex_test.py Normal file → Executable file
View File

@ -11,11 +11,22 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
@unittest.skip("SKIPPING 22_sram_func_test") @unittest.skip("SKIPPING 22_sram_pex_test")
class sram_func_test(openram_test): class sram_func_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.use_pex = True
# This is a hack to reload the characterizer __init__ with the spice version
from importlib import reload
import characterizer
reload(characterizer)
from characterizer import setup_hold
if not OPTS.spice_exe:
debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1)
global verify global verify
import verify import verify
@ -31,14 +42,10 @@ class sram_func_test(openram_test):
import tech import tech
debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank")
OPTS.check_lvsdrc = False
OPTS.use_pex = True
s = sram.sram(word_size=OPTS.word_size, s = sram.sram(word_size=OPTS.word_size,
num_words=OPTS.num_words, num_words=OPTS.num_words,
num_banks=OPTS.num_banks, num_banks=OPTS.num_banks,
name="test_sram1") name="test_sram1")
OPTS.check_lvsdrc = True
OPTS.use_pex = False
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds" tempgds = OPTS.openram_temp + "temp.gds"
@ -90,7 +97,7 @@ class sram_func_test(openram_test):
self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"]) self.assertTrue(round(value1) > 0.5 * tech.spice["supply_voltage"])
self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"]) self.assertTrue(round(value2) < 0.5 * tech.spice["supply_voltage"])
OPTS.check_lvsdrc = True
def convert_voltage_unit(self, string): def convert_voltage_unit(self, string):
newstring = "" newstring = ""

View File

@ -11,12 +11,11 @@ import globals
from globals import OPTS from globals import OPTS
import debug import debug
@unittest.skip("SKIPPING 22_sram_func_test")
class sram_func_test(openram_test): class sram_func_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="" # Unset to use any simulator
OPTS.analytical_delay = False OPTS.analytical_delay = False
# This is a hack to reload the characterizer __init__ with the spice version # This is a hack to reload the characterizer __init__ with the spice version
@ -35,8 +34,6 @@ class sram_func_test(openram_test):
num_banks=1, num_banks=1,
name="sram_func_test") name="sram_func_test")
OPTS.check_lvsdrc = True
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
@ -45,7 +42,7 @@ class sram_func_test(openram_test):
debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data))
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0]) corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
d = delay.delay(s,tempspice,corner) d = delay(s,tempspice,corner)
d.set_probe(probe_address,probe_data) d.set_probe(probe_address,probe_data)
# This will exit if it doesn't find a feasible period # This will exit if it doesn't find a feasible period
@ -55,7 +52,7 @@ class sram_func_test(openram_test):
feasible_period = d.find_feasible_period() feasible_period = d.find_feasible_period()
os.remove(tempspice) os.remove(tempspice)
OPTS.analytical_delay = True
reload(characterizer) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -15,7 +15,6 @@ class lib_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
import sram import sram
from characterizer import lib from characterizer import lib
@ -25,8 +24,7 @@ class lib_test(openram_test):
num_words=16, num_words=16,
num_banks=1, num_banks=1,
name="sram_2_16_1_{0}".format(OPTS.tech_name)) name="sram_2_16_1_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = True
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)

View File

@ -15,8 +15,6 @@ class lib_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="" # Unset to use any simulator
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.trim_netlist = True OPTS.trim_netlist = True
@ -35,7 +33,6 @@ class lib_test(openram_test):
num_words=16, num_words=16,
num_banks=1, num_banks=1,
name="sram_2_16_1_{0}".format(OPTS.tech_name)) name="sram_2_16_1_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = True
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
@ -54,7 +51,6 @@ class lib_test(openram_test):
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),newname)
self.isapproxdiff(libname,golden,0.40) self.isapproxdiff(libname,golden,0.40)
OPTS.analytical_delay = True
reload(characterizer) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -15,8 +15,6 @@ class lib_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = False
OPTS.spice_name="" # Unset to use any simulator
OPTS.analytical_delay = False OPTS.analytical_delay = False
OPTS.trim_netlist = False OPTS.trim_netlist = False
@ -35,7 +33,6 @@ class lib_test(openram_test):
num_words=16, num_words=16,
num_banks=1, num_banks=1,
name="sram_2_16_1_{0}".format(OPTS.tech_name)) name="sram_2_16_1_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = True
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
s.sp_write(tempspice) s.sp_write(tempspice)
@ -53,8 +50,6 @@ class lib_test(openram_test):
golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename) golden = "{0}/golden/{1}".format(os.path.dirname(os.path.realpath(__file__)),filename)
self.isapproxdiff(libname,golden,0.40) self.isapproxdiff(libname,golden,0.40)
OPTS.analytical_delay = True
OPTS.trim_netlist = True
reload(characterizer) reload(characterizer)
globals.end_openram() globals.end_openram()

View File

@ -15,8 +15,6 @@ class lef_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
# we will manually run lvs/drc
OPTS.check_lvsdrc = False
import sram import sram
@ -26,8 +24,6 @@ class lef_test(openram_test):
num_banks=OPTS.num_banks, num_banks=OPTS.num_banks,
name="sram_2_16_1_{0}".format(OPTS.tech_name)) name="sram_2_16_1_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = True
gdsfile = s.name + ".gds" gdsfile = s.name + ".gds"
leffile = s.name + ".lef" leffile = s.name + ".lef"
gdsname = OPTS.openram_temp + gdsfile gdsname = OPTS.openram_temp + gdsfile

View File

@ -15,8 +15,6 @@ class verilog_test(openram_test):
def runTest(self): def runTest(self):
globals.init_openram("config_20_{0}".format(OPTS.tech_name)) globals.init_openram("config_20_{0}".format(OPTS.tech_name))
# we will manually run lvs/drc
OPTS.check_lvsdrc = False
import sram import sram
@ -26,8 +24,6 @@ class verilog_test(openram_test):
num_banks=OPTS.num_banks, num_banks=OPTS.num_banks,
name="sram_2_16_1_{0}".format(OPTS.tech_name)) name="sram_2_16_1_{0}".format(OPTS.tech_name))
OPTS.check_lvsdrc = True
vfile = s.name + ".v" vfile = s.name + ".v"
vname = OPTS.openram_temp + vfile vname = OPTS.openram_temp + vfile
s.verilog_write(vname) s.verilog_write(vname)

View File

@ -12,6 +12,7 @@ sys.path.append(os.path.join(sys.path[0],".."))
import globals import globals
from globals import OPTS from globals import OPTS
import debug import debug
import getpass
class openram_test(openram_test): class openram_test(openram_test):
@ -72,10 +73,7 @@ class openram_test(openram_test):
shutil.rmtree(out_path, ignore_errors=True) shutil.rmtree(out_path, ignore_errors=True)
self.assertEqual(os.path.exists(out_path),False) self.assertEqual(os.path.exists(out_path),False)
# The default was on, so disable it.
OPTS.check_lvsdrc=False
globals.end_openram() globals.end_openram()
OPTS.check_lvsdrc=True
# instantiate a copy of the class to actually run the test # instantiate a copy of the class to actually run the test
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -27,5 +27,13 @@ modules = map(__import__, moduleNames)
suite = unittest.TestSuite() suite = unittest.TestSuite()
load = unittest.defaultTestLoader.loadTestsFromModule load = unittest.defaultTestLoader.loadTestsFromModule
suite.addTests(map(load, modules)) suite.addTests(map(load, modules))
ret = not unittest.TextTestRunner(verbosity=2).run(suite).wasSuccessful()
sys.exit(ret) test_runner = unittest.TextTestRunner(verbosity=2,stream=sys.stderr)
test_result = test_runner.run(suite)
import verify
verify.print_drc_stats()
verify.print_lvs_stats()
verify.print_pex_stats()
sys.exit(not test_result.wasSuccessful())

View File

@ -1,5 +1,5 @@
import unittest,warnings import unittest,warnings
import sys,os,glob import sys,os,glob,copy
sys.path.append(os.path.join(sys.path[0],"..")) sys.path.append(os.path.join(sys.path[0],".."))
from globals import OPTS from globals import OPTS
import debug import debug
@ -8,17 +8,23 @@ class openram_test(unittest.TestCase):
""" Base unit test that we have some shared classes in. """ """ Base unit test that we have some shared classes in. """
def local_drc_check(self, w): def local_drc_check(self, w):
self.reset()
tempgds = OPTS.openram_temp + "temp.gds" tempgds = OPTS.openram_temp + "temp.gds"
w.gds_write(tempgds) w.gds_write(tempgds)
import verify import verify
self.assertFalse(verify.run_drc(w.name, tempgds))
files = glob.glob(OPTS.openram_temp + '*') result=verify.run_drc(w.name, tempgds)
for f in files: if result != 0:
os.remove(f) self.fail("DRC failed: {}".format(a.name))
self.cleanup()
def local_check(self, a, final_verification=False): def local_check(self, a, final_verification=False):
self.reset()
tempspice = OPTS.openram_temp + "temp.sp" tempspice = OPTS.openram_temp + "temp.sp"
tempgds = OPTS.openram_temp + "temp.gds" tempgds = OPTS.openram_temp + "temp.gds"
@ -27,19 +33,12 @@ class openram_test(unittest.TestCase):
import verify import verify
result=verify.run_drc(a.name, tempgds) result=verify.run_drc(a.name, tempgds)
self.reset() if result != 0:
try:
self.assertTrue(result==0)
except:
self.fail("DRC failed: {}".format(a.name)) self.fail("DRC failed: {}".format(a.name))
result=verify.run_lvs(a.name, tempgds, tempspice, final_verification) result=verify.run_lvs(a.name, tempgds, tempspice, final_verification)
self.reset() if result != 0:
try:
self.assertTrue(result==0)
except:
self.reset()
self.fail("LVS mismatch: {}".format(a.name)) self.fail("LVS mismatch: {}".format(a.name))
if OPTS.purge_temp: if OPTS.purge_temp:
@ -54,9 +53,14 @@ class openram_test(unittest.TestCase):
os.remove(f) os.remove(f)
def reset(self): def reset(self):
""" Reset the static duplicate name checker for unit tests """ """
import design Reset everything after each test.
design.design.name_map=[] """
# Reset the static duplicate name checker for unit tests.
import hierarchy_design
hierarchy_design.hierarchy_design.name_map=[]
def isclose(self, value1,value2,error_tolerance=1e-2): def isclose(self, value1,value2,error_tolerance=1e-2):
""" This is used to compare relative values. """ """ This is used to compare relative values. """

View File

@ -13,7 +13,7 @@ import debug
from globals import OPTS,find_exe,get_tool from globals import OPTS,find_exe,get_tool
import sys import sys
debug.info(2,"Initializing verify...") debug.info(1,"Initializing verify...")
if not OPTS.check_lvsdrc: if not OPTS.check_lvsdrc:
debug.info(1,"LVS/DRC/PEX disabled.") debug.info(1,"LVS/DRC/PEX disabled.")
@ -21,6 +21,7 @@ if not OPTS.check_lvsdrc:
OPTS.lvs_exe = None OPTS.lvs_exe = None
OPTS.pex_exe = None OPTS.pex_exe = None
else: else:
debug.info(1, "Finding DRC/LVS/PEX tools.")
OPTS.drc_exe = get_tool("DRC",["calibre","assura","magic"]) OPTS.drc_exe = get_tool("DRC",["calibre","assura","magic"])
OPTS.lvs_exe = get_tool("LVS",["calibre","assura","netgen"]) OPTS.lvs_exe = get_tool("LVS",["calibre","assura","netgen"])
OPTS.pex_exe = get_tool("PEX",["calibre","magic"]) OPTS.pex_exe = get_tool("PEX",["calibre","magic"])
@ -31,22 +32,22 @@ if OPTS.check_lvsdrc and OPTS.tech_name == "freepdk45":
if OPTS.drc_exe == None: if OPTS.drc_exe == None:
pass pass
elif "calibre"==OPTS.drc_exe[0]: elif "calibre"==OPTS.drc_exe[0]:
from .calibre import run_drc from .calibre import run_drc,print_drc_stats
elif "assura"==OPTS.drc_exe[0]: elif "assura"==OPTS.drc_exe[0]:
from .assura import run_drc from .assura import run_drc,print_drc_stats
elif "magic"==OPTS.drc_exe[0]: elif "magic"==OPTS.drc_exe[0]:
from .magic import run_drc from .magic import run_drc,print_drc_stats
else: else:
debug.warning("Did not find a supported DRC tool.") debug.warning("Did not find a supported DRC tool.")
if OPTS.lvs_exe == None: if OPTS.lvs_exe == None:
pass pass
elif "calibre"==OPTS.lvs_exe[0]: elif "calibre"==OPTS.lvs_exe[0]:
from .calibre import run_lvs from .calibre import run_lvs,print_lvs_stats
elif "assura"==OPTS.lvs_exe[0]: elif "assura"==OPTS.lvs_exe[0]:
from .assura import run_lvs from .assura import run_lvs,print_lvs_stats
elif "netgen"==OPTS.lvs_exe[0]: elif "netgen"==OPTS.lvs_exe[0]:
from .magic import run_lvs from .magic import run_lvs,print_lvs_stats
else: else:
debug.warning("Did not find a supported LVS tool.") debug.warning("Did not find a supported LVS tool.")
@ -54,9 +55,9 @@ else:
if OPTS.pex_exe == None: if OPTS.pex_exe == None:
pass pass
elif "calibre"==OPTS.pex_exe[0]: elif "calibre"==OPTS.pex_exe[0]:
from .calibre import run_pex from .calibre import run_pex,print_pex_stats
elif "magic"==OPTS.pex_exe[0]: elif "magic"==OPTS.pex_exe[0]:
from .magic import run_pex from .magic import run_pex,print_pex_stats
else: else:
debug.warning("Did not find a supported PEX tool.") debug.warning("Did not find a supported PEX tool.")

View File

@ -25,10 +25,18 @@ import time
import debug import debug
from globals import OPTS from globals import OPTS
# Keep track of statistics
num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def run_drc(name, gds_name): def run_drc(name, gds_name):
"""Run DRC check on a given top-level name which is """Run DRC check on a given top-level name which is
implemented in gds_name.""" implemented in gds_name."""
global num_drc_runs
num_drc_runs += 1
from tech import drc from tech import drc
drc_rules = drc["drc_rules"] drc_rules = drc["drc_rules"]
drc_runset = OPTS.openram_temp + name + ".rsf" drc_runset = OPTS.openram_temp + name + ".rsf"
@ -88,6 +96,10 @@ def run_drc(name, gds_name):
def run_lvs(name, gds_name, sp_name): def run_lvs(name, gds_name, sp_name):
"""Run LVS check on a given top-level name which is """Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. """ implemented in gds_name and sp_name. """
global num_lvs_runs
num_lvs_runs += 1
from tech import drc from tech import drc
lvs_rules = drc["lvs_rules"] lvs_rules = drc["lvs_rules"]
lvs_runset = OPTS.openram_temp + name + ".rsf" lvs_runset = OPTS.openram_temp + name + ".rsf"
@ -170,3 +182,13 @@ def run_pex(name, gds_name, sp_name, output=None):
"""Run pex on a given top-level name which is """Run pex on a given top-level name which is
implemented in gds_name and sp_name. """ implemented in gds_name and sp_name. """
debug.error("PEX extraction not implemented with Assura.",-1) debug.error("PEX extraction not implemented with Assura.",-1)
global num_pex_runs
num_pex_runs += 1
def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs))

View File

@ -65,10 +65,17 @@ import debug
from globals import OPTS from globals import OPTS
import subprocess import subprocess
# Keep track of statistics
num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def run_drc(cell_name, gds_name): def run_drc(cell_name, gds_name):
"""Run DRC check on a given top-level name which is """Run DRC check on a given top-level name which is
implemented in gds_name.""" implemented in gds_name."""
global num_drc_runs
num_drc_runs += 1
# the runset file contains all the options to run calibre # the runset file contains all the options to run calibre
from tech import drc from tech import drc
@ -141,7 +148,10 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
"""Run LVS check on a given top-level name which is """Run LVS check on a given top-level name which is
implemented in gds_name and sp_name. Final verification will implemented in gds_name and sp_name. Final verification will
ensure that there are no remaining virtual conections. """ ensure that there are no remaining virtual conections. """
global num_lvs_runs
num_lvs_runs += 1
from tech import drc from tech import drc
lvs_rules = drc["lvs_rules"] lvs_rules = drc["lvs_rules"]
lvs_runset = { lvs_runset = {
@ -258,6 +268,10 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
def run_pex(cell_name, gds_name, sp_name, output=None): def run_pex(cell_name, gds_name, sp_name, output=None):
"""Run pex on a given top-level name which is """Run pex on a given top-level name which is
implemented in gds_name and sp_name. """ implemented in gds_name and sp_name. """
global num_pex_runs
num_pex_runs += 1
from tech import drc from tech import drc
if output == None: if output == None:
output = name + ".pex.netlist" output = name + ".pex.netlist"
@ -354,3 +368,10 @@ def correct_port(name, output_file_name, ref_file_name):
output_file.write(circuit_title) output_file.write(circuit_title)
output_file.write(part2) output_file.write(part2)
output_file.close() output_file.close()
def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs))

View File

@ -64,6 +64,11 @@ import debug
from globals import OPTS from globals import OPTS
import subprocess import subprocess
# Keep track of statistics
num_drc_runs = 0
num_lvs_runs = 0
num_pex_runs = 0
def write_magic_script(cell_name, gds_name, extract=False): def write_magic_script(cell_name, gds_name, extract=False):
""" Write a magic script to perform DRC and optionally extraction. """ """ Write a magic script to perform DRC and optionally extraction. """
@ -148,6 +153,9 @@ def write_netgen_script(cell_name, sp_name):
def run_drc(cell_name, gds_name, extract=False): def run_drc(cell_name, gds_name, extract=False):
"""Run DRC check on a cell which is implemented in gds_name.""" """Run DRC check on a cell which is implemented in gds_name."""
global num_drc_runs
num_drc_runs += 1
write_magic_script(cell_name, gds_name, extract) write_magic_script(cell_name, gds_name, extract)
# run drc # run drc
@ -198,6 +206,9 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False):
implemented in gds_name and sp_name. Final verification will implemented in gds_name and sp_name. Final verification will
ensure that there are no remaining virtual conections. """ ensure that there are no remaining virtual conections. """
global num_lvs_runs
num_lvs_runs += 1
run_drc(cell_name, gds_name, extract=True) run_drc(cell_name, gds_name, extract=True)
write_netgen_script(cell_name, sp_name) write_netgen_script(cell_name, sp_name)
@ -270,6 +281,9 @@ def run_pex(name, gds_name, sp_name, output=None):
"""Run pex on a given top-level name which is """Run pex on a given top-level name which is
implemented in gds_name and sp_name. """ implemented in gds_name and sp_name. """
global num_pex_runs
num_pex_runs += 1
debug.warning("PEX using magic not implemented.") debug.warning("PEX using magic not implemented.")
return 1 return 1
@ -337,3 +351,9 @@ def run_pex(name, gds_name, sp_name, output=None):
return out_errors return out_errors
def print_drc_stats():
debug.info(1,"DRC runs: {0}".format(num_drc_runs))
def print_lvs_stats():
debug.info(1,"LVS runs: {0}".format(num_lvs_runs))
def print_pex_stats():
debug.info(1,"PEX runs: {0}".format(num_pex_runs))