From b3732f4fcf7c8c0bf39e93f28fdfe781fa80a792 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 09:51:28 -0700 Subject: [PATCH 1/6] Output debug warnings and errors to stderr. Clean up regress script a bit. --- compiler/debug.py | 6 +++--- compiler/tests/regress.py | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/compiler/debug.py b/compiler/debug.py index 7001373e..1bf46db0 100644 --- a/compiler/debug.py +++ b/compiler/debug.py @@ -13,19 +13,19 @@ def check(check,str): (frame, filename, line_number, function_name, lines, index) = inspect.getouterframes(inspect.currentframe())[1] 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 def error(str,return_value=0): (frame, filename, line_number, function_name, lines, 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 def warning(str): (frame, filename, line_number, function_name, lines, 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): diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index 73ec0d7f..93989248 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -28,5 +28,6 @@ suite = unittest.TestSuite() load = unittest.defaultTestLoader.loadTestsFromModule 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) +sys.exit(not test_result.wasSuccessful()) From f894ef47afffdd84ee4ebd340e25cc3853d58970 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 11:58:22 -0700 Subject: [PATCH 2/6] Fix missing list conversion to run drc library tests. --- compiler/tests/01_library_drc_test.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index a3c07d47..b4d35a5f 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -13,14 +13,12 @@ class library_drc_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) - global verify import verify - OPTS.check_lvsdrc=True - + (gds_dir, gds_files) = setup_files() drc_errors = 0 debug.info(1, "\nPerforming DRC on: " + ", ".join(gds_files)) - for f in gds_files: + for f in list(gds_files): name = re.sub('\.gds$', '', f) gds_name = "{0}/{1}".format(gds_dir, f) if not os.path.isfile(gds_name): @@ -32,11 +30,12 @@ class library_drc_test(openram_test): self.assertEqual(drc_errors, 0) globals.end_openram() + def setup_files(): gds_dir = OPTS.openram_tech + "/gds_lib" files = os.listdir(gds_dir) nametest = re.compile("\.gds$", re.IGNORECASE) - gds_files = filter(nametest.search, files) + gds_files = list(filter(nametest.search, files)) return (gds_dir, gds_files) @@ -46,3 +45,4 @@ if __name__ == "__main__": del sys.argv[1:] header(__file__, OPTS.tech_name) unittest.main() + From 58646ab8e6007eacc057b8d450b8b9afde6014c3 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 11:59:24 -0700 Subject: [PATCH 3/6] Add DRC/LVS/PEX statistics in verbose=1 mode --- compiler/tests/regress.py | 6 ++++++ compiler/verify/__init__.py | 19 ++++++++++--------- compiler/verify/assura.py | 22 ++++++++++++++++++++++ compiler/verify/calibre.py | 23 ++++++++++++++++++++++- compiler/verify/magic.py | 20 ++++++++++++++++++++ 5 files changed, 80 insertions(+), 10 deletions(-) diff --git a/compiler/tests/regress.py b/compiler/tests/regress.py index 93989248..d97def2b 100755 --- a/compiler/tests/regress.py +++ b/compiler/tests/regress.py @@ -30,4 +30,10 @@ suite.addTests(map(load, modules)) 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()) diff --git a/compiler/verify/__init__.py b/compiler/verify/__init__.py index a83629b0..2199e765 100644 --- a/compiler/verify/__init__.py +++ b/compiler/verify/__init__.py @@ -13,7 +13,7 @@ import debug from globals import OPTS,find_exe,get_tool import sys -debug.info(2,"Initializing verify...") +debug.info(1,"Initializing verify...") if not OPTS.check_lvsdrc: debug.info(1,"LVS/DRC/PEX disabled.") @@ -21,6 +21,7 @@ if not OPTS.check_lvsdrc: OPTS.lvs_exe = None OPTS.pex_exe = None else: + debug.info(1, "Finding DRC/LVS/PEX tools.") OPTS.drc_exe = get_tool("DRC",["calibre","assura","magic"]) OPTS.lvs_exe = get_tool("LVS",["calibre","assura","netgen"]) 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: pass 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]: - from .assura import run_drc + from .assura import run_drc,print_drc_stats elif "magic"==OPTS.drc_exe[0]: - from .magic import run_drc + from .magic import run_drc,print_drc_stats else: debug.warning("Did not find a supported DRC tool.") if OPTS.lvs_exe == None: pass 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]: - from .assura import run_lvs + from .assura import run_lvs,print_lvs_stats elif "netgen"==OPTS.lvs_exe[0]: - from .magic import run_lvs + from .magic import run_lvs,print_lvs_stats else: debug.warning("Did not find a supported LVS tool.") @@ -54,9 +55,9 @@ else: if OPTS.pex_exe == None: pass 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]: - from .magic import run_pex + from .magic import run_pex,print_pex_stats else: debug.warning("Did not find a supported PEX tool.") diff --git a/compiler/verify/assura.py b/compiler/verify/assura.py index 27949721..d3c605f3 100644 --- a/compiler/verify/assura.py +++ b/compiler/verify/assura.py @@ -25,10 +25,18 @@ import time import debug 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): """Run DRC check on a given top-level name which is implemented in gds_name.""" + global num_drc_runs + num_drc_runs += 1 + from tech import drc drc_rules = drc["drc_rules"] 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): """Run LVS check on a given top-level name which is implemented in gds_name and sp_name. """ + + global num_lvs_runs + num_lvs_runs += 1 + from tech import drc lvs_rules = drc["lvs_rules"] 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 implemented in gds_name and sp_name. """ 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)) diff --git a/compiler/verify/calibre.py b/compiler/verify/calibre.py index 38e92a2c..a7cd9290 100644 --- a/compiler/verify/calibre.py +++ b/compiler/verify/calibre.py @@ -65,10 +65,17 @@ import debug from globals import OPTS 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): """Run DRC check on a given top-level name which is implemented in gds_name.""" + + global num_drc_runs + num_drc_runs += 1 # the runset file contains all the options to run calibre from tech import drc @@ -141,7 +148,10 @@ def run_lvs(cell_name, gds_name, sp_name, final_verification=False): """Run LVS check on a given top-level name which is implemented in gds_name and sp_name. Final verification will ensure that there are no remaining virtual conections. """ - + + global num_lvs_runs + num_lvs_runs += 1 + from tech import drc lvs_rules = drc["lvs_rules"] 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): """Run pex on a given top-level name which is implemented in gds_name and sp_name. """ + + global num_pex_runs + num_pex_runs += 1 + from tech import drc if output == None: 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(part2) 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)) diff --git a/compiler/verify/magic.py b/compiler/verify/magic.py index f438bad8..319d159a 100644 --- a/compiler/verify/magic.py +++ b/compiler/verify/magic.py @@ -64,6 +64,11 @@ import debug from globals import OPTS 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): """ 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): """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) # 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 ensure that there are no remaining virtual conections. """ + global num_lvs_runs + num_lvs_runs += 1 + run_drc(cell_name, gds_name, extract=True) 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 implemented in gds_name and sp_name. """ + global num_pex_runs + num_pex_runs += 1 + debug.warning("PEX using magic not implemented.") return 1 @@ -337,3 +351,9 @@ def run_pex(name, gds_name, sp_name, output=None): 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)) From 265b5d977a0acd6ec8703f465e3c695944518776 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 12:00:15 -0700 Subject: [PATCH 4/6] Fix option reload problems and checkpointing so that it works properly. --- compiler/characterizer/__init__.py | 9 ++++++--- compiler/globals.py | 14 +++++++++++--- compiler/tests/02_library_lvs_test.py | 2 +- compiler/tests/21_hspice_delay_test.py | 2 +- compiler/tests/21_hspice_setuphold_test.py | 2 -- compiler/tests/21_ngspice_delay_test.py | 1 - compiler/tests/22_pex_test.py | 13 +++++++++++-- compiler/tests/30_openram_test.py | 3 --- 8 files changed, 30 insertions(+), 16 deletions(-) diff --git a/compiler/characterizer/__init__.py b/compiler/characterizer/__init__.py index 6930a112..dc9398d0 100644 --- a/compiler/characterizer/__init__.py +++ b/compiler/characterizer/__init__.py @@ -1,16 +1,18 @@ import os import debug +import globals from globals import OPTS,find_exe,get_tool from .lib import * from .delay import * from .setup_hold import * -debug.info(2,"Initializing characterizer...") - +debug.info(1,"Initializing characterizer...") OPTS.spice_exe = "" if not OPTS.analytical_delay: + debug.info(1, "Finding spice simulator.") + if OPTS.spice_name != "": OPTS.spice_exe=find_exe(OPTS.spice_name) if OPTS.spice_exe=="": @@ -24,6 +26,7 @@ if not OPTS.analytical_delay: if OPTS.spice_exe == "": debug.error("No recognizable spice version found. Unable to perform characterization.",1) - +else: + debug.info(1,"Analytical model enabled.") diff --git a/compiler/globals.py b/compiler/globals.py index 8c305664..90f85f70 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -155,7 +155,7 @@ def read_config(config_file, is_unit_test=True): # 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 = copy.deepcopy(CHECKPOINT_OPTS) + OPTS.__dict__ = CHECKPOINT_OPTS.__dict__.copy() return # Create a full path relative to current dir unless it is already an abs path @@ -213,12 +213,17 @@ def read_config(config_file, is_unit_test=True): # Make a checkpoint of the options so we can restore # after each unit test - CHECKPOINT_OPTS = copy.deepcopy(OPTS) - + if not CHECKPOINT_OPTS: + CHECKPOINT_OPTS = copy.copy(OPTS) def end_openram(): """ Clean up openram for a proper exit """ cleanup_paths() + + import verify + verify.print_drc_stats() + verify.print_lvs_stats() + verify.print_pex_stats() @@ -227,6 +232,7 @@ def cleanup_paths(): """ We should clean up the temp directory after execution. """ + global OPTS if not OPTS.purge_temp: debug.info(0,"Preserving temp directory: {}".format(OPTS.openram_temp)) return @@ -341,6 +347,8 @@ def print_time(name, now_time, last_time=None): def report_status(): """ 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 if type(OPTS.word_size)!=int: debug.error("{0} is not an integer in config file.".format(OPTS.word_size)) diff --git a/compiler/tests/02_library_lvs_test.py b/compiler/tests/02_library_lvs_test.py index d85f284e..0b35f159 100755 --- a/compiler/tests/02_library_lvs_test.py +++ b/compiler/tests/02_library_lvs_test.py @@ -14,7 +14,7 @@ class library_lvs_test(openram_test): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) import verify - OPTS.check_lvsdrc=True + (gds_dir, sp_dir, allnames) = setup_files() lvs_errors = 0 debug.info(1, "Performing LVS on: " + ", ".join(allnames)) diff --git a/compiler/tests/21_hspice_delay_test.py b/compiler/tests/21_hspice_delay_test.py index b170b072..289f3934 100755 --- a/compiler/tests/21_hspice_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -17,6 +17,7 @@ class timing_sram_test(openram_test): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) OPTS.spice_name="hspice" OPTS.analytical_delay = False + # This is a hack to reload the characterizer __init__ with the spice version from importlib import reload @@ -83,7 +84,6 @@ class timing_sram_test(openram_test): else: self.isclose(data[k],golden_data[k],0.15) - reload(characterizer) globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/21_hspice_setuphold_test.py b/compiler/tests/21_hspice_setuphold_test.py index 5c4f72a6..f9d8a01c 100755 --- a/compiler/tests/21_hspice_setuphold_test.py +++ b/compiler/tests/21_hspice_setuphold_test.py @@ -26,7 +26,6 @@ class timing_setup_test(openram_test): if not OPTS.spice_exe: debug.error("Could not find {} simulator.".format(OPTS.spice_name),-1) - import sram import tech slews = [tech.spice["rise_time"]*2] @@ -58,7 +57,6 @@ class timing_setup_test(openram_test): else: self.isclose(data[k],golden_data[k],0.15) - reload(characterizer) globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py index 36e0c0d0..739d63ea 100755 --- a/compiler/tests/21_ngspice_delay_test.py +++ b/compiler/tests/21_ngspice_delay_test.py @@ -83,7 +83,6 @@ class timing_sram_test(openram_test): else: self.isclose(data[k],golden_data[k],0.15) - reload(characterizer) globals.end_openram() # instantiate a copdsay of the class to actually run the test diff --git a/compiler/tests/22_pex_test.py b/compiler/tests/22_pex_test.py index 1ccf6cfe..24cb7733 100755 --- a/compiler/tests/22_pex_test.py +++ b/compiler/tests/22_pex_test.py @@ -16,6 +16,17 @@ class sram_func_test(openram_test): def runTest(self): 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 import verify @@ -31,8 +42,6 @@ class sram_func_test(openram_test): import tech debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") - global OPTS - OPTS.use_pex = True s = sram.sram(word_size=OPTS.word_size, num_words=OPTS.num_words, num_banks=OPTS.num_banks, diff --git a/compiler/tests/30_openram_test.py b/compiler/tests/30_openram_test.py index df480fdc..51546ae3 100755 --- a/compiler/tests/30_openram_test.py +++ b/compiler/tests/30_openram_test.py @@ -73,10 +73,7 @@ class openram_test(openram_test): shutil.rmtree(out_path, ignore_errors=True) self.assertEqual(os.path.exists(out_path),False) - # The default was on, so disable it. - OPTS.check_lvsdrc=False globals.end_openram() - OPTS.check_lvsdrc=True # instantiate a copy of the class to actually run the test if __name__ == "__main__": From 8a530da2ccb71164d28d34adbd0fbd248b5fc3a0 Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 12:07:37 -0700 Subject: [PATCH 5/6] Remove extra conversion to list --- compiler/tests/01_library_drc_test.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/tests/01_library_drc_test.py b/compiler/tests/01_library_drc_test.py index b4d35a5f..b809c14d 100755 --- a/compiler/tests/01_library_drc_test.py +++ b/compiler/tests/01_library_drc_test.py @@ -18,7 +18,7 @@ class library_drc_test(openram_test): (gds_dir, gds_files) = setup_files() drc_errors = 0 debug.info(1, "\nPerforming DRC on: " + ", ".join(gds_files)) - for f in list(gds_files): + for f in gds_files: name = re.sub('\.gds$', '', f) gds_name = "{0}/{1}".format(gds_dir, f) if not os.path.isfile(gds_name): From 7d8352a04d4f1f1e33609ac72ea93420fa16804f Mon Sep 17 00:00:00 2001 From: Matt Guthaus Date: Wed, 11 Jul 2018 12:12:03 -0700 Subject: [PATCH 6/6] Fix order of checkpointing so that it is done after characterizer and verify have found their executables. --- compiler/globals.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/compiler/globals.py b/compiler/globals.py index 90f85f70..34cfbe08 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -105,6 +105,7 @@ def check_versions(): def init_openram(config_file, is_unit_test=True): """Initialize the technology, paths, simulators, etc.""" + check_versions() debug.info(1,"Initializing OpenRAM...") @@ -119,6 +120,26 @@ def init_openram(config_file, is_unit_test=True): 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): @@ -147,16 +168,6 @@ def read_config(config_file, is_unit_test=True): reads will just restore the previous copy (ask mrg) """ 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 # Create a full path relative to current dir unless it is already an abs path if not os.path.isabs(config_file): @@ -211,10 +222,6 @@ def read_config(config_file, is_unit_test=True): except: debug.error("Unable to make output directory.",-1) - # Make a checkpoint of the options so we can restore - # after each unit test - if not CHECKPOINT_OPTS: - CHECKPOINT_OPTS = copy.copy(OPTS) def end_openram(): """ Clean up openram for a proper exit """