diff --git a/compiler/globals.py b/compiler/globals.py index 996025d7..405fd5c6 100644 --- a/compiler/globals.py +++ b/compiler/globals.py @@ -185,37 +185,48 @@ def set_spice(): debug.info(2,"Finding spice...") global OPTS - # set the input dir for spice files if using ngspice (not needed for - # hspice) + OPTS.spice_exe = "" + + # Check if the preferred spice option exists in the path + for path in os.environ["PATH"].split(os.pathsep): + spice_exe = os.path.join(path, OPTS.spice_version) + # if it is found, then break and use first version + if is_exe(spice_exe): + debug.info(1, "Using spice: " + spice_exe) + OPTS.spice_exe = spice_exe + break + + if not OPTS.force_spice: + # if we didn't find the preferred version, try the other version and warn + prev_version=OPTS.spice_version + if OPTS.spice_version == "hspice": + OPTS.spice_version = "ngspice" + else: + OPTS.spice_version = "hspice" + debug.warning("Unable to find {0} so trying {1}".format(prev_version,OPTS.spice_version)) + + for path in os.environ["PATH"].split(os.pathsep): + spice_exe = os.path.join(path, OPTS.spice_version) + # if it is found, then break and use first version + if is_exe(spice_exe): + found_spice = True + debug.info(1, "Using spice: " + spice_exe) + OPTS.spice_exe = spice_exe + break + + # set the input dir for spice files if using ngspice if OPTS.spice_version == "ngspice": os.environ["NGSPICE_INPUT_DIR"] = "{0}".format(OPTS.openram_temp) - for path in os.environ["PATH"].split(os.pathsep): - OPTS.spice_exe = os.path.join(path, OPTS.spice_version) - # if it is found, then break and use first version - if is_exe(OPTS.spice_exe): - debug.info(1, "Using spice: " + OPTS.spice_exe) - return - - # if we didn't find the previous version, try the other version - if OPTS.spice_version == "hspice": - OPTS.spice_version = "ngspice" - else: - OPTS.spice_version = "hspice" - debug.warning("Unable to find spice so trying other: " + OPTS.spice_version) - - for path in os.environ["PATH"].split(os.pathsep): - OPTS.spice_exe = os.path.join(path, OPTS.spice_version) - # if it is found, then break and use first version - if is_exe(OPTS.spice_exe): - found_spice = True - debug.info(1, "Using spice: " + OPTS.spice_exe) - break - else: + if OPTS.spice_exe == "": # otherwise, give warning and procede - debug.warning("Spice not found. Unable to perform characterization.") - + if OPTS.force_spice: + debug.error("{0} not found. Unable to perform characterization.".format(OPTS.spice_version),1) + else: + debug.error("Neither hspice/ngspice not found. Unable to perform characterization.",1) + + # imports correct technology directories for testing def import_tech(): global OPTS diff --git a/compiler/options.py b/compiler/options.py index 616630a0..0b03933e 100644 --- a/compiler/options.py +++ b/compiler/options.py @@ -18,6 +18,8 @@ class options(optparse.Values): check_lvsdrc = True # Variable to select the variant of spice (hspice or ngspice right now) spice_version = "hspice" + # Should we fall back if we can't find our preferred spice? + force_spice = False # Should we print out the banner at startup print_banner = True # The Calibre executable being used which is derived from the user PATH. diff --git a/compiler/tests/21_timing_delay_test.py b/compiler/tests/21_hspice_delay_test.py similarity index 95% rename from compiler/tests/21_timing_delay_test.py rename to compiler/tests/21_hspice_delay_test.py index 8a129203..e2245afc 100644 --- a/compiler/tests/21_timing_delay_test.py +++ b/compiler/tests/21_hspice_delay_test.py @@ -23,7 +23,10 @@ class timing_sram_test(unittest.TestCase): # we will manually run lvs/drc OPTS.check_lvsdrc = False OPTS.use_pex = False - + OPTS.spice_version="hspice" + OPTS.force_spice = True + globals.set_spice() + import sram debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") diff --git a/compiler/tests/21_timing_hold_test.py b/compiler/tests/21_hspice_hold_test.py similarity index 94% rename from compiler/tests/21_timing_hold_test.py rename to compiler/tests/21_hspice_hold_test.py index 78d4fe5b..74fe2e7d 100644 --- a/compiler/tests/21_timing_hold_test.py +++ b/compiler/tests/21_hspice_hold_test.py @@ -23,6 +23,9 @@ class timing_setup_test(unittest.TestCase): # we will manually run lvs/drc OPTS.check_lvsdrc = False OPTS.use_pex = False + OPTS.spice_version="hspice" + OPTS.force_spice = True + globals.set_spice() import sram import setup_hold diff --git a/compiler/tests/21_timing_setup_test.py b/compiler/tests/21_hspice_setup_test.py similarity index 94% rename from compiler/tests/21_timing_setup_test.py rename to compiler/tests/21_hspice_setup_test.py index 4011d6ad..ac3e66ff 100644 --- a/compiler/tests/21_timing_setup_test.py +++ b/compiler/tests/21_hspice_setup_test.py @@ -20,9 +20,13 @@ class timing_setup_test(unittest.TestCase): def runTest(self): globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc OPTS.check_lvsdrc = False OPTS.use_pex = False + OPTS.spice_version="hspice" + OPTS.force_spice = True + globals.set_spice() import sram import setup_hold diff --git a/compiler/tests/21_ngspice_delay_test.py b/compiler/tests/21_ngspice_delay_test.py new file mode 100644 index 00000000..05287ee4 --- /dev/null +++ b/compiler/tests/21_ngspice_delay_test.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_sram_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + OPTS.spice_version="ngspice" + OPTS.force_spice = True + globals.set_spice() + + import sram + + debug.info(1, "Testing timing for sample 1bit, 16words SRAM with 1 bank") + s = sram.sram(word_size=OPTS.config.word_size, + num_words=OPTS.config.num_words, + num_banks=OPTS.config.num_banks, + name="test_sram1") + + OPTS.check_lvsdrc = True + + import delay + + tempspice = OPTS.openram_temp + "temp.sp" + s.sp_write(tempspice) + + probe_address = "1" * s.addr_size + probe_data = s.word_size - 1 + debug.info(1, "Probe address {0} probe data {1}".format(probe_address, probe_data)) + + d = delay.delay(s,tempspice) + data = d.analyze(probe_address, probe_data) + + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(data['delay1'],0.013649)) + self.assertTrue(isclose(data['delay0'],0.22893)) + self.assertTrue(isclose(data['min_period1'],0.078582763671875)) + self.assertTrue(isclose(data['min_period0'],0.25543212890625)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(data['delay1'],1.5335)) + self.assertTrue(isclose(data['delay0'],2.2635000000000005)) + self.assertTrue(isclose(data['min_period1'],1.53564453125)) + self.assertTrue(isclose(data['min_period0'],2.998046875)) + else: + self.assertTrue(False) # other techs fail + + os.remove(tempspice) + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/21_ngspice_hold_test.py b/compiler/tests/21_ngspice_hold_test.py new file mode 100644 index 00000000..4e7a92ce --- /dev/null +++ b/compiler/tests/21_ngspice_hold_test.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_setup_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + OPTS.spice_version="ngspice" + OPTS.force_spice = True + globals.set_spice() + + import sram + import setup_hold + + sh = setup_hold.setup_hold() + [one_setup_time, zero_setup_time] = sh.hold_time() + + OPTS.check_lvsdrc = True + + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(one_setup_time,-0.0048828125)) + self.assertTrue(isclose(zero_setup_time,-0.010986328125)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(one_setup_time,0.04638671875)) + self.assertTrue(isclose(zero_setup_time,-0.0830078125)) + else: + self.assertTrue(False) # other techs fail + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main() diff --git a/compiler/tests/21_ngspice_setup_test.py b/compiler/tests/21_ngspice_setup_test.py new file mode 100644 index 00000000..6b2fce53 --- /dev/null +++ b/compiler/tests/21_ngspice_setup_test.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python2.7 +""" +Run a regresion test on various srams +""" + +import unittest +from header import header +import sys,os +sys.path.append(os.path.join(sys.path[0],"..")) +import globals +import debug +import calibre + +OPTS = globals.get_opts() + +#@unittest.skip("SKIPPING 21_timing_sram_test") + + +class timing_setup_test(unittest.TestCase): + + def runTest(self): + globals.init_openram("config_20_{0}".format(OPTS.tech_name)) + # we will manually run lvs/drc + OPTS.check_lvsdrc = False + OPTS.use_pex = False + OPTS.spice_version="ngspice" + OPTS.force_spice = True + globals.set_spice() + + import sram + import setup_hold + + sh = setup_hold.setup_hold() + [one_setup_time, zero_setup_time] = sh.setup_time() + + OPTS.check_lvsdrc = True + if OPTS.tech_name == "freepdk45": + self.assertTrue(isclose(one_setup_time,0.0146484375)) + self.assertTrue(isclose(zero_setup_time,0.008544921875)) + elif OPTS.tech_name == "scn3me_subm": + self.assertTrue(isclose(one_setup_time,0.0927734375)) + self.assertTrue(isclose(zero_setup_time,-0.0244140625)) + else: + self.assertTrue(False) # other techs fail + +def isclose(value1,value2): + """ This is used to compare relative values for convergence. """ + return (abs(value1 - value2) / max(value1,value2) <= 1e-2) + + +# instantiate a copdsay of the class to actually run the test +if __name__ == "__main__": + (OPTS, args) = globals.parse_args() + del sys.argv[1:] + header(__file__, OPTS.tech_name) + unittest.main()