def test_read_libs(self) -> None: """ Test that HammerTool can read technology IP libraries and filter/process them. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir( "dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") HammerToolTestHelpers.write_tech_json(tech_json_filename) tech = self.get_tech( hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") class Tool(SingleStepTool): def step(self) -> bool: def test_tool_format(lib, filt) -> List[str]: return ["drink {0}".format(lib)] self._read_lib_output = tech.read_libs( [hammer_tech.filters.milkyway_techfile_filter], test_tool_format, must_exist=False) self._test_filter_output = tech.read_libs( [HammerToolTestHelpers.make_test_filter()], test_tool_format, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech database = hammer_config.HammerDatabase() test.set_database(database) tech.set_database(database) test.run() # Don't care about ordering here. self.assertEqual(set(test._read_lib_output), { "drink {0}/soy".format(tech_dir), "drink {0}/coconut".format(tech_dir) }) # We do care about ordering here. self.assertEqual(test._test_filter_output, [ "drink {0}/tea".format(tech_dir), "drink {0}/grapefruit".format(tech_dir), "drink {0}/juice".format(tech_dir), "drink {0}/orange".format(tech_dir) ]) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def __init__(self, test: unittest.TestCase, tool_type: str) -> None: self.test = test # type unittest.TestCase self.logger = HammerVLSILogging.context("") self._driver = None # type: Optional[hammer_vlsi.HammerDriver] if tool_type not in ["sram_generator"]: raise NotImplementedError("Have not created a test for %s yet" % (tool_type)) self._tool_type = tool_type
def test_macro_sizes(self) -> None: """ Test that getting macro sizes works as expected. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir( "dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") def add_lib_with_lef(d: Dict[str, Any]) -> Dict[str, Any]: with open(os.path.join(tech_dir, 'my_vendor_lib.lef'), 'w') as f: f.write("""VERSION 5.8 ; BUSBITCHARS "[]" ; DIVIDERCHAR "/" ; MACRO my_awesome_macro CLASS BLOCK ; ORIGIN -0.435 607.525 ; FOREIGN my_awesome_macro 0.435 -607.525 ; SIZE 810.522 BY 607.525 ; SYMMETRY X Y R90 ; END my_awesome_macro END LIBRARY """) r = deepdict(d) r['libraries'].append({ 'name': 'my_vendor_lib', 'lef file': 'test/my_vendor_lib.lef' }) return r HammerToolTestHelpers.write_tech_json(tech_json_filename, add_lib_with_lef) tech_opt = hammer_tech.HammerTechnology.load_from_dir( "dummy28", tech_dir) if tech_opt is None: self.assertTrue(False, "Unable to load technology") return else: tech = tech_opt # type: hammer_tech.HammerTechnology tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") database = hammer_config.HammerDatabase() tech.set_database(database) # Test that macro sizes can be read out of the LEF. self.assertEqual(tech.get_macro_sizes(), [ hammer_tech.MacroSize(library='my_vendor_lib', name='my_awesome_macro', width=Decimal("810.522"), height=Decimal("607.525")) ]) # Cleanup shutil.rmtree(tech_dir_base)
def __init__(self, test: unittest.TestCase, tool_type: str) -> None: self.test = test # type unittest.TestCase self.logger = HammerVLSILogging.context("") self._driver = None # type: Optional[hammer_vlsi.HammerDriver] if tool_type not in ["drc", "lvs"]: raise NotImplementedError("Have not created a test for %s yet" % (tool_type)) self._tool_type = tool_type
def test_read_libs(self) -> None: """ Test that HammerTool can read technology IP libraries and filter/process them. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") HammerToolTestHelpers.write_tech_json(tech_json_filename) tech = self.get_tech(hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") class Tool(SingleStepTool): def step(self) -> bool: def test_tool_format(lib, filt) -> List[str]: return ["drink {0}".format(lib)] self._read_lib_output = tech.read_libs([hammer_tech.filters.milkyway_techfile_filter], test_tool_format, must_exist=False) self._test_filter_output = tech.read_libs([HammerToolTestHelpers.make_test_filter()], test_tool_format, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech database = hammer_config.HammerDatabase() test.set_database(database) tech.set_database(database) test.run() # Don't care about ordering here. self.assertEqual(set(test._read_lib_output), {"drink {0}/soy".format(tech_dir), "drink {0}/coconut".format(tech_dir)}) # We do care about ordering here. self.assertEqual(test._test_filter_output, [ "drink {0}/tea".format(tech_dir), "drink {0}/grapefruit".format(tech_dir), "drink {0}/juice".format(tech_dir), "drink {0}/orange".format(tech_dir) ]) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def __init__(self, test: unittest.TestCase, cmd_type: str) -> None: self.echo_command_args = ["go", "bears", "!"] self.echo_command = ["echo"] + self.echo_command_args self.test = test # type unittest.TestCase self.logger = HammerVLSILogging.context("") self._driver = None # type: Optional[hammer_vlsi.HammerDriver] if cmd_type not in ["lsf", "local"]: raise NotImplementedError("Have not built a test for %s yet" % cmd_type) self._cmd_type = cmd_type self._submit_command = None # type: Optional[hammer_vlsi.HammerSubmitCommand]
def test_macro_sizes(self) -> None: """ Test that getting macro sizes works as expected. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") def add_lib_with_lef(d: Dict[str, Any]) -> Dict[str, Any]: with open(os.path.join(tech_dir, 'my_vendor_lib.lef'), 'w') as f: f.write("""VERSION 5.8 ; BUSBITCHARS "[]" ; DIVIDERCHAR "/" ; MACRO my_awesome_macro CLASS BLOCK ; ORIGIN -0.435 607.525 ; FOREIGN my_awesome_macro 0.435 -607.525 ; SIZE 810.522 BY 607.525 ; SYMMETRY X Y R90 ; END my_awesome_macro END LIBRARY """) r = deepdict(d) r['libraries'].append({ 'name': 'my_vendor_lib', 'lef file': 'test/my_vendor_lib.lef' }) return r HammerToolTestHelpers.write_tech_json(tech_json_filename, add_lib_with_lef) tech_opt = hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir) if tech_opt is None: self.assertTrue(False, "Unable to load technology") return else: tech = tech_opt # type: hammer_tech.HammerTechnology tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") database = hammer_config.HammerDatabase() tech.set_database(database) # Test that macro sizes can be read out of the LEF. self.assertEqual(tech.get_macro_sizes(), [ hammer_tech.MacroSize(library='my_vendor_lib', name='my_awesome_macro', width=810.522, height=607.525) ]) # Cleanup shutil.rmtree(tech_dir_base)
def test_file_logging(self): fd, path = tempfile.mkstemp(".log") os.close(fd) # Don't leak file descriptors filelogger = HammerVLSIFileLogger(path) HammerVLSILogging.clear_callbacks() HammerVLSILogging.add_callback(filelogger.callback) log = HammerVLSILogging.context() log.info("Hello world") log.info("Eternal voyage to the edge of the universe") filelogger.close() with open(path, 'r') as f: self.assertEqual(f.read().strip(), """ [<global>] Level.INFO: Hello world [<global>] Level.INFO: Eternal voyage to the edge of the universe """.strip()) # Remove temp file os.remove(path)
def test_colours(self): """ Test that we can log with and without colour. """ msg = "This is a test message" # type: str log = HammerVLSILogging.context("test") HammerVLSILogging.enable_buffering = True # we need this for test HammerVLSILogging.clear_callbacks() HammerVLSILogging.add_callback(HammerVLSILogging.callback_buffering) HammerVLSILogging.enable_colour = True log.info(msg) self.assertEqual( HammerVLSILogging.get_colour_escape(Level.INFO) + "[test] " + msg + HammerVLSILogging.COLOUR_CLEAR, HammerVLSILogging.get_buffer()[0]) HammerVLSILogging.enable_colour = False log.info(msg) self.assertEqual("[test] " + msg, HammerVLSILogging.get_buffer()[0])
def test_colours(self): """ Test that we can log with and without colour. """ msg = "This is a test message" # type: str log = HammerVLSILogging.context("test") HammerVLSILogging.enable_buffering = True # we need this for test HammerVLSILogging.clear_callbacks() HammerVLSILogging.add_callback(HammerVLSILogging.callback_buffering) HammerVLSILogging.enable_colour = True log.info(msg) self.assertEqual(HammerVLSILogging.get_colour_escape(Level.INFO) + "[test] " + msg + HammerVLSILogging.COLOUR_CLEAR, HammerVLSILogging.get_buffer()[0]) HammerVLSILogging.enable_colour = False log.info(msg) self.assertEqual("[test] " + msg, HammerVLSILogging.get_buffer()[0])
def test_subcontext(self): HammerVLSILogging.enable_colour = False HammerVLSILogging.enable_tag = True HammerVLSILogging.clear_callbacks() HammerVLSILogging.add_callback(HammerVLSILogging.callback_buffering) # Get top context log = HammerVLSILogging.context("top") # Create sub-contexts. logA = log.context("A") logB = log.context("B") msgA = "Hello world from A" msgB = "Hello world from B" logA.info(msgA) logB.error(msgB) self.assertEqual(HammerVLSILogging.get_buffer(), ['[top] [A] ' + msgA, '[top] [B] ' + msgB])
def test_subcontext(self): HammerVLSILogging.enable_colour = False HammerVLSILogging.enable_tag = True HammerVLSILogging.clear_callbacks() HammerVLSILogging.add_callback(HammerVLSILogging.callback_buffering) # Get top context log = HammerVLSILogging.context("top") # Create sub-contexts. logA = log.context("A") logB = log.context("B") msgA = "Hello world from A" msgB = "Hello world from B" logA.info(msgA) logB.error(msgB) self.assertEqual(HammerVLSILogging.get_buffer(), ['[top] [A] ' + msgA, '[top] [B] ' + msgB] )
def test_read_extra_libs(self) -> None: """ Test that HammerTool can read/process extra IP libraries in addition to those of the technology. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir( "dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") HammerToolTestHelpers.write_tech_json(tech_json_filename) tech = self.get_tech( hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("tech") class Tool(hammer_vlsi.DummyHammerTool): lib_output = [] # type: List[str] filter_output = [] # type: List[str] @property def steps(self) -> List[hammer_vlsi.HammerToolStep]: return self.make_steps_from_methods([self.step]) def step(self) -> bool: def test_tool_format(lib, filt) -> List[str]: return ["drink {0}".format(lib)] Tool.lib_output = tech.read_libs( [hammer_tech.filters.milkyway_techfile_filter], test_tool_format, must_exist=False) Tool.filter_output = tech.read_libs( [HammerToolTestHelpers.make_test_filter()], test_tool_format, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech # Add some extra libraries to see if they are picked up database = hammer_config.HammerDatabase() lib1_path = "/foo/bar" lib1b_path = "/library/specific/prefix" lib2_path = "/baz/quux" database.update_project([{ 'vlsi.technology.extra_libraries': [{ "library": { "milkyway techfile": "test/xylophone" } }, { "library": { "openaccess techfile": "test/orange" } }, { "prefix": { "prefix": "lib1", "path": lib1_path }, "library": { "milkyway techfile": "lib1/muffin" } }, { "prefix": { "prefix": "lib1", "path": lib1b_path }, "library": { "milkyway techfile": "lib1/granola" } }, { "prefix": { "prefix": "lib2", "path": lib2_path }, "library": { "openaccess techfile": "lib2/cake", "milkyway techfile": "lib2/brownie", "provides": [{ "lib_type": "stdcell" }] } }] }]) test.set_database(database) tech.set_database(database) test.run() # Not testing ordering in this assertion. self.assertEqual( set(Tool.lib_output), { "drink {0}/soy".format(tech_dir), "drink {0}/coconut".format(tech_dir), "drink {0}/xylophone".format(tech_dir), "drink {0}/muffin".format(lib1_path), "drink {0}/granola".format(lib1b_path), "drink {0}/brownie".format(lib2_path) }) # We do care about ordering here. # Our filter should put the techfile first and sort the rest. print("Tool.filter_output = " + str(Tool.filter_output)) tech_lef_result = [ # tech lef "drink {0}/tea".format(tech_dir) ] base_lib_results = [ "drink {0}/grapefruit".format(tech_dir), "drink {0}/juice".format(tech_dir), "drink {0}/orange".format(tech_dir) ] extra_libs_results = ["drink {0}/cake".format(lib2_path)] self.assertEqual( Tool.filter_output, tech_lef_result + sorted(base_lib_results + extra_libs_results)) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def test_timing_lib_ecsm_filter(self) -> None: """ Test that the ECSM-first filter works as expected. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir( "dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") tech_json = { "name": "dummy28", "installs": [{ "path": "test", "base var": "" # means relative to tech dir }], "libraries": [{ "ecsm liberty file": "test/eggs.ecsm", "ccs liberty file": "test/eggs.ccs", "nldm liberty file": "test/eggs.nldm" }, { "ccs liberty file": "test/custard.ccs", "nldm liberty file": "test/custard.nldm" }, { "nldm liberty file": "test/noodles.nldm" }, { "ecsm liberty file": "test/eggplant.ecsm" }, { "ccs liberty file": "test/cookies.ccs" }] } with open(tech_json_filename, "w") as f: f.write(json.dumps(tech_json, indent=4)) tech = self.get_tech( hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") class Tool(SingleStepTool): lib_outputs = [] # type: List[str] def step(self) -> bool: Tool.lib_outputs = tech.read_libs( [hammer_tech.filters.timing_lib_with_ecsm_filter], hammer_tech.HammerTechnologyUtils.to_plain_item, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech test.set_database(hammer_config.HammerDatabase()) tech.set_database(hammer_config.HammerDatabase()) test.run() # Check that the ecsm-based filter prioritized ecsm -> ccs -> nldm. self.assertEqual( set(Tool.lib_outputs), { "{0}/eggs.ecsm".format(tech_dir), "{0}/custard.ccs".format(tech_dir), "{0}/noodles.nldm".format(tech_dir), "{0}/eggplant.ecsm".format(tech_dir), "{0}/cookies.ccs".format(tech_dir) }) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def test_timing_lib_ecsm_filter(self) -> None: """ Test that the ECSM-first filter works as expected. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") tech_json = { "name": "dummy28", "installs": [ { "path": "test", "base var": "" # means relative to tech dir } ], "libraries": [ { "ecsm liberty file": "test/eggs.ecsm", "ccs liberty file": "test/eggs.ccs", "nldm liberty file": "test/eggs.nldm" }, { "ccs liberty file": "test/custard.ccs", "nldm liberty file": "test/custard.nldm" }, { "nldm liberty file": "test/noodles.nldm" }, { "ecsm liberty file": "test/eggplant.ecsm" }, { "ccs liberty file": "test/cookies.ccs" } ] } with open(tech_json_filename, "w") as f: f.write(json.dumps(tech_json, indent=4)) tech = self.get_tech(hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") class Tool(SingleStepTool): lib_outputs = [] # type: List[str] def step(self) -> bool: Tool.lib_outputs = tech.read_libs([hammer_tech.filters.timing_lib_with_ecsm_filter], hammer_tech.HammerTechnologyUtils.to_plain_item, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech test.set_database(hammer_config.HammerDatabase()) tech.set_database(hammer_config.HammerDatabase()) test.run() # Check that the ecsm-based filter prioritized ecsm -> ccs -> nldm. self.assertEqual(set(Tool.lib_outputs), { "{0}/eggs.ecsm".format(tech_dir), "{0}/custard.ccs".format(tech_dir), "{0}/noodles.nldm".format(tech_dir), "{0}/eggplant.ecsm".format(tech_dir), "{0}/cookies.ccs".format(tech_dir) }) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def test_read_extra_libs(self) -> None: """ Test that HammerTool can read/process extra IP libraries in addition to those of the technology. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") HammerToolTestHelpers.write_tech_json(tech_json_filename) tech = self.get_tech(hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("tech") class Tool(hammer_vlsi.DummyHammerTool): lib_output = [] # type: List[str] filter_output = [] # type: List[str] @property def steps(self) -> List[hammer_vlsi.HammerToolStep]: return self.make_steps_from_methods([ self.step ]) def step(self) -> bool: def test_tool_format(lib, filt) -> List[str]: return ["drink {0}".format(lib)] Tool.lib_output = tech.read_libs([hammer_tech.filters.milkyway_techfile_filter], test_tool_format, must_exist=False) Tool.filter_output = tech.read_libs([HammerToolTestHelpers.make_test_filter()], test_tool_format, must_exist=False) return True test = Tool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech # Add some extra libraries to see if they are picked up database = hammer_config.HammerDatabase() lib1_path = "/foo/bar" lib1b_path = "/library/specific/prefix" lib2_path = "/baz/quux" database.update_project([{ 'vlsi.technology.extra_libraries': [ { "library": {"milkyway techfile": "test/xylophone"} }, { "library": {"openaccess techfile": "test/orange"} }, { "prefix": { "prefix": "lib1", "path": lib1_path }, "library": {"milkyway techfile": "lib1/muffin"} }, { "prefix": { "prefix": "lib1", "path": lib1b_path }, "library": {"milkyway techfile": "lib1/granola"} }, { "prefix": { "prefix": "lib2", "path": lib2_path }, "library": { "openaccess techfile": "lib2/cake", "milkyway techfile": "lib2/brownie", "provides": [ {"lib_type": "stdcell"} ] } } ] }]) test.set_database(database) tech.set_database(database) test.run() # Not testing ordering in this assertion. self.assertEqual(set(Tool.lib_output), { "drink {0}/soy".format(tech_dir), "drink {0}/coconut".format(tech_dir), "drink {0}/xylophone".format(tech_dir), "drink {0}/muffin".format(lib1_path), "drink {0}/granola".format(lib1b_path), "drink {0}/brownie".format(lib2_path) }) # We do care about ordering here. # Our filter should put the techfile first and sort the rest. print("Tool.filter_output = " + str(Tool.filter_output)) tech_lef_result = [ # tech lef "drink {0}/tea".format(tech_dir) ] base_lib_results = [ "drink {0}/grapefruit".format(tech_dir), "drink {0}/juice".format(tech_dir), "drink {0}/orange".format(tech_dir) ] extra_libs_results = [ "drink {0}/cake".format(lib2_path) ] self.assertEqual(Tool.filter_output, tech_lef_result + sorted(base_lib_results + extra_libs_results)) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)
def __init__(self, options: HammerDriverOptions, extra_project_config: dict = {}) -> None: """ Create a hammer-vlsi driver, which is a higher level convenience function for quickly using hammer-vlsi. It imports and uses the hammer-vlsi blocks. Set up logging, databases, context, etc. :param options: Driver options. :param extra_project_config: An extra flattened config for the project. Optional. """ # Create global logging context. file_logger = HammerVLSIFileLogger(options.log_file) HammerVLSILogging.add_callback(file_logger.callback) self.log = HammerVLSILogging.context( ) # type: HammerVLSILoggingContext # Create a new hammer database. self.database = hammer_config.HammerDatabase( ) # type: hammer_config.HammerDatabase self.log.info("Loading hammer-vlsi libraries and reading settings") # Store the run dir. self.obj_dir = options.obj_dir # type: str # Load in builtins. self.database.update_builtins([ hammer_config.load_config_from_file(os.path.join( HammerVLSISettings.hammer_vlsi_path, "builtins.yml"), strict=True), HammerVLSISettings.get_config() ]) # Read in core defaults. self.database.update_core( hammer_config.load_config_from_defaults( HammerVLSISettings.hammer_vlsi_path)) # Read in the environment config for paths to CAD tools, etc. for config in options.environment_configs: if not os.path.exists(config): self.log.error("Environment config %s does not exist!" % (config)) self.database.update_environment( hammer_config.load_config_from_paths(options.environment_configs, strict=True)) # Read in the project config to find the syn, par, and tech. project_configs = hammer_config.load_config_from_paths( options.project_configs, strict=True) project_configs.append(extra_project_config) self.project_configs = [] # type: List[dict] self.update_project_configs(project_configs) # Get the technology and load technology settings. self.tech = None # type: Optional[hammer_tech.HammerTechnology] self.load_technology() # Keep track of what the synthesis and par configs are since # update_tools() just takes a whole list. self.tool_configs = {} # type: Dict[str, List[dict]] # Initialize tool fields. self.syn_tool = None # type: Optional[HammerSynthesisTool] self.par_tool = None # type: Optional[HammerPlaceAndRouteTool] # Initialize tool hooks. Used to specify resume/pause hooks after custom hooks have been registered. self.post_custom_syn_tool_hooks = [ ] # type: List[HammerToolHookAction] self.post_custom_par_tool_hooks = [ ] # type: List[HammerToolHookAction]
def __init__(self, options: HammerDriverOptions, extra_project_config: dict = {}) -> None: """ Create a hammer-vlsi driver, which is a higher level convenience function for quickly using hammer-vlsi. It imports and uses the hammer-vlsi blocks. Set up logging, databases, context, etc. :param options: Driver options. :param extra_project_config: An extra flattened config for the project. Optional. """ # Create global logging context. file_logger = HammerVLSIFileLogger(options.log_file) HammerVLSILogging.add_callback(file_logger.callback) self.log = HammerVLSILogging.context() # type: HammerVLSILoggingContext # Create a new hammer database. self.database = hammer_config.HammerDatabase() # type: hammer_config.HammerDatabase self.log.info("Loading hammer-vlsi libraries and reading settings") # Store the run dir. self.obj_dir = options.obj_dir # type: str # Load builtins and core into the database. HammerVLSISettings.load_builtins_and_core(self.database) # Read in the environment config for paths to CAD tools, etc. for config in options.environment_configs: if not os.path.exists(config): self.log.error("Environment config %s does not exist!" % (config)) self.database.update_environment(hammer_config.load_config_from_paths(options.environment_configs, strict=True)) # Read in the project config to find the syn, par, and tech. project_configs = hammer_config.load_config_from_paths(options.project_configs, strict=True) project_configs.append(extra_project_config) self.project_configs = [] # type: List[dict] self.update_project_configs(project_configs) # Get the technology and load technology settings. self.tech = None # type: Optional[hammer_tech.HammerTechnology] self.load_technology() # Keep track of what the synthesis and par configs are since # update_tools() just takes a whole list. self.tool_configs = {} # type: Dict[str, List[dict]] # Initialize tool fields. self.syn_tool = None # type: Optional[HammerSynthesisTool] self.par_tool = None # type: Optional[HammerPlaceAndRouteTool] self.drc_tool = None # type: Optional[HammerDRCTool] self.lvs_tool = None # type: Optional[HammerLVSTool] self.sram_generator_tool = None # type: Optional[HammerSRAMGeneratorTool] # Initialize tool hooks. Used to specify resume/pause hooks after custom hooks have been registered. self.post_custom_syn_tool_hooks = [] # type: List[HammerToolHookAction] self.post_custom_par_tool_hooks = [] # type: List[HammerToolHookAction] self.post_custom_drc_tool_hooks = [] # type: List[HammerToolHookAction] self.post_custom_lvs_tool_hooks = [] # type: List[HammerToolHookAction] self.post_custom_sram_generator_tool_hooks = [] # type: List[HammerToolHookAction] self.post_custom_pcb_tool_hooks = [] # type: List[HammerToolHookAction]
def test_bumps(self) -> None: """ Test that HammerTool bump support works. """ import hammer_config tech_dir, tech_dir_base = HammerToolTestHelpers.create_tech_dir("dummy28") tech_json_filename = os.path.join(tech_dir, "dummy28.tech.json") HammerToolTestHelpers.write_tech_json(tech_json_filename) tech = self.get_tech(hammer_tech.HammerTechnology.load_from_dir("dummy28", tech_dir)) tech.cache_dir = tech_dir tech.logger = HammerVLSILogging.context("") test = DummyTool() test.logger = HammerVLSILogging.context("") test.run_dir = tempfile.mkdtemp() test.technology = tech database = hammer_config.HammerDatabase() # Check bad mode string doesn't look at null dict settings = """ { "vlsi.inputs.bumps_mode": "auto" } """ database.update_project([hammer_config.load_config_from_string(settings, is_yaml=False)]) test.set_database(database) with HammerLoggingCaptureContext() as c: my_bumps = test.get_bumps() self.assertTrue(c.log_contains("Invalid bumps_mode")) assert my_bumps is None, "Invalid bumps_mode should assume empty bumps" settings = """ { "vlsi.inputs.bumps_mode": "manual", "vlsi.inputs.bumps": { "x": 14, "y": 14, "pitch": 200, "cell": "MY_REDACTED_BUMP_CELL", "assignments": [ {"name": "reset", "x": 5, "y": 3}, {"no_connect": true, "x": 5, "y": 4}, {"name": "VDD", "x": 2, "y": 1}, {"name": "VSS", "x": 1, "y": 1}, {"name": "VSS", "no_connect": true, "x": 2, "y": 2}, {"x": 3, "y": 3}, {"name": "VSS", "x": 14, "y": 14} ] } } """ database.update_project([hammer_config.load_config_from_string(settings, is_yaml=False)]) test.set_database(database) with HammerLoggingCaptureContext() as c: my_bumps = test.get_bumps() self.assertTrue(c.log_contains("Invalid bump assignment")) assert my_bumps is not None # Only one of the assignments is invalid so the above 7 becomes 6 self.assertEqual(len(my_bumps.assignments), 6) # Cleanup shutil.rmtree(tech_dir_base) shutil.rmtree(test.run_dir)