mirror of https://github.com/VLSIDA/OpenRAM.git
merging changes with pbitcell_array test
This commit is contained in:
commit
ba43b986ae
|
|
@ -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"))
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,45 +7,23 @@ 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."""
|
||||||
|
|
@ -69,62 +47,6 @@ class design(hierarchy_spice.spice, hierarchy_layout.layout):
|
||||||
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()
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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.")
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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,12 +160,12 @@ 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
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
@ -194,11 +223,15 @@ def read_config(config_file, is_unit_test=True):
|
||||||
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))
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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"])
|
||||||
|
|
|
||||||
|
|
@ -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,6 +114,74 @@ 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)
|
||||||
|
|
|
||||||
|
|
@ -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 = {}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
python tests/regress.py -t freepdk45
|
|
||||||
python tests/regress.py -t scn3me_subm
|
|
||||||
754
compiler/sram.py
754
compiler/sram.py
|
|
@ -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,415 +184,68 @@ 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])
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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,50 +416,8 @@ 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):
|
||||||
|
|
@ -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):
|
||||||
|
|
|
||||||
|
|
@ -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()])
|
||||||
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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])
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ 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
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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__":
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ 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
|
||||||
import characterizer
|
import characterizer
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 = ""
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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,7 +24,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)
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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__":
|
||||||
|
|
|
||||||
|
|
@ -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())
|
||||||
|
|
|
||||||
|
|
@ -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. """
|
||||||
|
|
|
||||||
|
|
@ -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.")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
|
|
@ -65,11 +65,18 @@ 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
|
||||||
drc_rules = drc["drc_rules"]
|
drc_rules = drc["drc_rules"]
|
||||||
|
|
@ -142,6 +149,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
|
||||||
|
|
||||||
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))
|
||||||
|
|
|
||||||
|
|
@ -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))
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue