Skywater changes.

Default 1 thread and no temp subdirectory.
Add skywater setup/hold golden data
Add CLI option for simulation threads (-m)
Add compatibility mode option and nomodcheck for ngspice to speed up sky130 model loading.
Make subdir when using default /tmp dir.
Pass num_threads so temp subdirs are created.
This commit is contained in:
mrg 2021-03-08 14:40:36 -08:00
parent b6f3fbdd1f
commit 671470f5f2
18 changed files with 91 additions and 36 deletions

View File

@ -19,7 +19,7 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: scn4me_subm Archives
path: ${{ github.workspace }}/scn4me_subm_temp/*/*
path: $OPENRAM_HOME/*.zip
freepdk45:
runs-on: self-hosted
steps:
@ -38,7 +38,7 @@ jobs:
uses: actions/upload-artifact@v2
with:
name: FreePDK45 Archives
path: ${{ github.workspace }}/freepdk45_temp/*/*
path: $OPENRAM_HOME/*.zip
# coverage_stats:
# if: ${{ always() }}
# needs: [scn4me_subm, freepdk45]

View File

@ -31,6 +31,8 @@ def parse_spice_list(filename, key):
f = open(full_filename, "r")
except IOError:
debug.error("Unable to open spice output file: {0}".format(full_filename),1)
debug.archive()
contents = f.read()
f.close()
# val = re.search(r"{0}\s*=\s*(-?\d+.?\d*\S*)\s+.*".format(key), contents)

View File

@ -360,6 +360,8 @@ class stimuli():
# -r {2}timing.raw
ng_cfg = open("{}.spiceinit".format(OPTS.openram_temp), "w")
ng_cfg.write("set num_threads={}\n".format(OPTS.num_sim_threads))
ng_cfg.write("set ngbehavior=hsa\n")
ng_cfg.write("set ng_nomodcheck\n")
ng_cfg.close()
cmd = "{0} -b -o {2}timing.lis {1}".format(OPTS.spice_exe,

View File

@ -109,6 +109,19 @@ def info(lev, str):
frm[0].f_code.co_name, str))
def archive():
from globals import OPTS
try:
OPENRAM_HOME = os.path.abspath(os.environ.get("OPENRAM_HOME"))
except:
error("$OPENRAM_HOME is not properly defined.", 1)
import shutil
zip_file = "{0}/{1}_{2}".format(OPENRAM_HOME, "fail_", os.getpid())
info(0, "Archiving failed files to {}.zip".format(zip_file))
shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
def bp():
"""
An empty function so you can set soft breakpoints in pdb.

View File

@ -61,8 +61,13 @@ def parse_args():
optparse.make_option("-j", "--threads",
action="store",
type="int",
help="Specify the number of threads (default: 2)",
help="Specify the number of threads (default: 1)",
dest="num_threads"),
optparse.make_option("-m", "--sim_threads",
action="store",
type="int",
help="Specify the number of spice simulation threads (default: 2)",
dest="num_sim_threads"),
optparse.make_option("-v",
"--verbose",
action="count",
@ -381,6 +386,10 @@ def purge_temp():
""" Remove the temp directory. """
debug.info(1,
"Purging temp directory: {}".format(OPTS.openram_temp))
#import inspect
#s = inspect.stack()
#print("Purge {0} in dir {1}".format(s[3].filename, OPTS.openram_temp))
# This annoyingly means you have to re-cd into
# the directory each debug iteration
# shutil.rmtree(OPTS.openram_temp, ignore_errors=True)
@ -429,9 +438,15 @@ def setup_paths():
if "__pycache__" not in full_path:
sys.path.append("{0}".format(full_path))
# Use a unique temp subdirectory
OPTS.openram_temp += "/openram_{0}_{1}_temp/".format(getpass.getuser(),
# Use a unique temp subdirectory if multithreaded
if OPTS.num_threads > 1 or OPTS.openram_temp == "/tmp":
# Make a unique subdir
tempdir = "/openram_{0}_{1}_temp".format(getpass.getuser(),
os.getpid())
# Only add the unique subdir one time
if tempdir not in OPTS.openram_temp:
OPTS.openram_temp += tempdir
if not OPTS.openram_temp.endswith('/'):
OPTS.openram_temp += "/"
@ -470,6 +485,12 @@ def init_paths():
except OSError as e:
if e.errno == 17: # errno.EEXIST
os.chmod(OPTS.openram_temp, 0o750)
#import inspect
#s = inspect.stack()
#from pprint import pprint
#pprint(s)
#print("Test {0} in dir {1}".format(s[2].filename, OPTS.openram_temp))
# Don't delete the output dir, it may have other files!
# make the directory if it doesn't exist

View File

@ -526,13 +526,16 @@ class bank(design.design):
height=self.dff.height)
elif self.col_addr_size == 2:
self.column_decoder = factory.create(module_type="hierarchical_predecode2x4",
column_decoder=True,
height=self.dff.height)
elif self.col_addr_size == 3:
self.column_decoder = factory.create(module_type="hierarchical_predecode3x8",
column_decoder=True,
height=self.dff.height)
elif self.col_addr_size == 4:
self.column_decoder = factory.create(module_type="hierarchical_predecode4x16",
column_decoder=True,
height=self.dff.height)
else:
# No error checking before?

View File

@ -18,19 +18,17 @@ class hierarchical_predecode(design.design):
"""
Pre 2x4 and 3x8 and TBD 4x16 decoder shared code.
"""
def __init__(self, name, input_number, height=None):
def __init__(self, name, input_number, column_decoder=False, height=None):
self.number_of_inputs = input_number
b = factory.create(module_type=OPTS.bitcell)
if not height:
self.cell_height = b.height
self.column_decoder = False
else:
self.cell_height = height
# If we are pitch matched to the bitcell, it's a predecoder
# otherwise it's a column decoder (out of pgates)
self.column_decoder = (height != b.height)
self.column_decoder = column_decoder
self.number_of_outputs = int(math.pow(2, self.number_of_inputs))
super().__init__(name)
@ -87,8 +85,15 @@ class hierarchical_predecode(design.design):
self.bus_layer = layer_props.hierarchical_predecode.bus_layer
self.bus_directions = layer_props.hierarchical_predecode.bus_directions
if self.column_decoder:
# Column decoders may be routed on M2/M3 if there's a write mask
self.bus_pitch = self.m3_pitch
self.bus_space = self.m3_space
else:
self.bus_pitch = getattr(self, self.bus_layer + "_pitch")
self.bus_space = layer_props.hierarchical_predecode.bus_space_factor * getattr(self, self.bus_layer + "_space")
self.bus_space = getattr(self, self.bus_layer + "_space")
self.bus_space = layer_props.hierarchical_predecode.bus_space_factor * self.bus_space
self.input_layer = layer_props.hierarchical_predecode.input_layer
self.output_layer = layer_props.hierarchical_predecode.output_layer
self.output_layer_pitch = getattr(self, self.output_layer + "_pitch")

View File

@ -13,8 +13,8 @@ class hierarchical_predecode2x4(hierarchical_predecode):
"""
Pre 2x4 decoder used in hierarchical_decoder.
"""
def __init__(self, name, height=None):
super().__init__( name, 2, height)
def __init__(self, name, column_decoder=False, height=None):
super().__init__(name, 2, column_decoder, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -13,8 +13,8 @@ class hierarchical_predecode3x8(hierarchical_predecode):
"""
Pre 3x8 decoder used in hierarchical_decoder.
"""
def __init__(self, name, height=None):
super().__init__(name, 3, height)
def __init__(self, name, column_decoder=False, height=None):
super().__init__(name, 3, column_decoder, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -13,8 +13,8 @@ class hierarchical_predecode4x16(hierarchical_predecode):
"""
Pre 4x16 decoder used in hierarchical_decoder.
"""
def __init__(self, name, height=None):
super().__init__(name, 4, height)
def __init__(self, name, column_decoder=False, height=None):
super().__init__(name, 4, column_decoder, height)
self.create_netlist()
if not OPTS.netlist_only:

View File

@ -133,7 +133,7 @@ class options(optparse.Values):
magic_exe = None
# Number of threads to use
num_threads = 2
num_threads = 1
# Number of threads to use in ngspice/hspice
num_sim_threads = 2

View File

@ -12,8 +12,7 @@ import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class timing_setup_test(openram_test):
@ -29,14 +28,12 @@ class timing_setup_test(openram_test):
import characterizer
reload(characterizer)
from characterizer import setup_hold
import sram
import tech
slews = [tech.spice["rise_time"]*2]
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sh = setup_hold(corner)
data = sh.analyze(slews,slews)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'hold_times_HL': [-0.0158691],
'hold_times_LH': [-0.0158691],
@ -47,6 +44,11 @@ class timing_setup_test(openram_test):
'hold_times_LH': [-0.11718749999999999],
'setup_times_HL': [0.16357419999999998],
'setup_times_LH': [0.1757812]}
elif OPTS.tech_name == "sky130":
golden_data = {'hold_times_HL': [-0.05615234],
'hold_times_LH': [-0.03173828],
'setup_times_HL': [0.078125],
'setup_times_LH': [0.1025391]}
else:
self.assertTrue(False) # other techs fail

View File

@ -12,8 +12,7 @@ import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from globals import OPTS
from sram_factory import factory
import debug
class timing_setup_test(openram_test):
@ -29,14 +28,12 @@ class timing_setup_test(openram_test):
import characterizer
reload(characterizer)
from characterizer import setup_hold
import sram
import tech
slews = [tech.spice["rise_time"]*2]
corner = (OPTS.process_corners[0], OPTS.supply_voltages[0], OPTS.temperatures[0])
sh = setup_hold(corner)
data = sh.analyze(slews,slews)
#print data
if OPTS.tech_name == "freepdk45":
golden_data = {'hold_times_HL': [-0.01586914],
'hold_times_LH': [-0.01586914],
@ -47,6 +44,11 @@ class timing_setup_test(openram_test):
'hold_times_LH': [-0.1293945],
'setup_times_HL': [0.1757812],
'setup_times_LH': [0.1879883]}
elif OPTS.tech_name == "sky130":
golden_data = {'hold_times_HL': [-0.05615234],
'hold_times_LH': [-0.03173828],
'setup_times_HL': [0.078125],
'setup_times_LH': [0.1025391]}
else:
self.assertTrue(False) # other techs fail

View File

@ -49,6 +49,9 @@ class openram_back_end_test(openram_test):
if OPTS.tech_name:
options += " -t {}".format(OPTS.tech_name)
if OPTS.num_threads:
options += " -j {}".format(OPTS.num_threads)
# Always perform code coverage
if OPTS.coverage == 0:
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")

View File

@ -49,6 +49,9 @@ class openram_front_end_test(openram_test):
if OPTS.tech_name:
options += " -t {}".format(OPTS.tech_name)
if OPTS.num_threads:
options += " -j {}".format(OPTS.num_threads)
# Always perform code coverage
if OPTS.coverage == 0:
debug.warning("Failed to find coverage installation. This can be installed with pip3 install coverage")

View File

@ -13,7 +13,6 @@ import sys, os
sys.path.append(os.getenv("OPENRAM_HOME"))
import globals
from subunit import ProtocolTestCase, TestProtocolClient
from subunit.test_results import AutoTimingTestResultDecorator
from testtools import ConcurrentTestSuite
(OPTS, args) = globals.parse_args()
@ -71,7 +70,7 @@ def fork_tests(num_threads):
stream = os.fdopen(c2pwrite, 'wb', 0)
os.close(c2pread)
sys.stdin.close()
test_suite_result = AutoTimingTestResultDecorator(TestProtocolClient(stream))
test_suite_result = TestProtocolClient(stream)
test_suite.run(test_suite_result)
except EBADF:
try:

View File

@ -28,8 +28,7 @@ class openram_test(unittest.TestCase):
result=verify.run_drc(w.name, tempgds, None)
if result != 0:
self.fail("DRC failed: {}".format(w.name))
if not OPTS.keep_temp:
elif not OPTS.keep_temp:
self.cleanup()
def local_check(self, a, final_verification=False):
@ -74,10 +73,10 @@ class openram_test(unittest.TestCase):
# shutil.make_archive(zip_file, 'zip', OPTS.openram_temp)
self.fail("LVS mismatch: {}".format(a.name))
if lvs_result == 0 and drc_result == 0 and not OPTS.keep_temp:
self.cleanup()
# For debug...
# import pdb; pdb.set_trace()
if not OPTS.keep_temp:
self.cleanup()
def run_pex(self, a, output=None):
tempspice = "{}.sp".format(a.name)
@ -104,6 +103,7 @@ class openram_test(unittest.TestCase):
def cleanup(self):
""" Reset the duplicate checker and cleanup files. """
files = glob.glob(OPTS.openram_temp + '*')
for f in files:
# Only remove the files