From f8c80d1f4caf2994b750f03379908f3373e5d47b Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Wed, 17 Apr 2024 10:33:55 -0700 Subject: [PATCH 1/9] Fixes to minor scripting issue --- e2e/configs-tool/cm.yml | 2 +- e2e/src/amscf_template.scs | 19 + hammer/sim/xcelium/__init__.py | 757 +++++++++++++++++++++++++- hammer/sim/xcelium/defaults.yml | 33 +- hammer/sim/xcelium/defaults_types.yml | 29 +- 5 files changed, 817 insertions(+), 23 deletions(-) create mode 100644 e2e/src/amscf_template.scs diff --git a/e2e/configs-tool/cm.yml b/e2e/configs-tool/cm.yml index df7785586..3474639f4 100644 --- a/e2e/configs-tool/cm.yml +++ b/e2e/configs-tool/cm.yml @@ -4,7 +4,7 @@ vlsi.core.build_system: make # Select tools vlsi.core.synthesis_tool: "hammer.synthesis.genus" vlsi.core.par_tool: "hammer.par.innovus" -vlsi.core.sim_tool: "hammer.sim.vcs" +vlsi.core.sim_tool: "hammer.sim.xcelium" vlsi.core.timing_tool: "hammer.timing.tempus" vlsi.core.formal_tool: "hammer.formal.conformal" diff --git a/e2e/src/amscf_template.scs b/e2e/src/amscf_template.scs new file mode 100644 index 000000000..dfa74f351 --- /dev/null +++ b/e2e/src/amscf_template.scs @@ -0,0 +1,19 @@ +simulator lang=spectre + +// global signals + +// model deck + +// analog control file +include "acf.scs" + +// schematic deck + +// ams configuration options +amsd { + // set the connect rule power supply + + // mix analog and digital + // portmap subckt=cell_name + // config cell=cell_name use=spice +} diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index d2f035298..9867a0dea 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -15,6 +15,8 @@ import json import datetime import io +import re +import logging # Remove later to use hammer logging from typing import Dict, List, Optional, Tuple, Any import hammer.tech as hammer_tech @@ -23,6 +25,7 @@ from hammer.logging import HammerVLSILogging from hammer.common.cadence import CadenceTool +# MXHammer version class xcelium(HammerSimTool, CadenceTool): @@ -30,17 +33,20 @@ class xcelium(HammerSimTool, CadenceTool): def xcelium_ext(self) -> List[str]: verilog_ext = [".v", ".V", ".VS", ".vp", ".VP"] sverilog_ext = [".sv",".SV",".svp",".SVP",".svi",".svh",".vlib",".VLIB"] + verilogams_ext = [".vams", ".VAMS", ".Vams", ".vAMS"] + vhdl_ext = [".vhdl", ".VHDL"] + scs_ext = [".scs", ".SCS", ".sp", ".SP"] c_cxx_ext = [".c",".cc",".cpp"] - gz_ext = [ext + ".gz" for ext in verilog_ext + sverilog_ext] - z_ext = [ext + ".z" for ext in verilog_ext + sverilog_ext] - return (verilog_ext + sverilog_ext + c_cxx_ext + gz_ext + z_ext) + gz_ext = [ext + ".gz" for ext in verilog_ext + sverilog_ext + verilogams_ext + scs_ext] + z_ext = [ext + ".z" for ext in verilog_ext + sverilog_ext + verilogams_ext + scs_ext] + return (verilog_ext + sverilog_ext + verilogams_ext + vhdl_ext + scs_ext + c_cxx_ext + gz_ext + z_ext) @property def steps(self) -> List[HammerToolStep]: return self.make_steps_from_methods([self.compile_xrun, self.elaborate_xrun, self.sim_xrun]) - + def tool_config_prefix(self) -> str: return "sim.xcelium" @@ -55,6 +61,10 @@ def sim_waveform_prefix(self) -> str: @property def xcelium_bin(self) -> str: return self.get_setting("sim.xcelium.xcelium_bin") + + @property + def spectre_bin(self) -> str: + return self.get_setting("sim.xcelium.spectre_bin") @property def sim_tcl_file(self) -> str: @@ -107,7 +117,7 @@ def extract_xrun_opts(self) -> Tuple[Dict[str, str], Dict[str, str]]: xrun_opts = self.get_settings_from_dict(xrun_opts_def ,key_prefix=self.tool_config_prefix()) xrun_opts_proc = xrun_opts.copy() - bool_list = ["global_access", "enhanced_recompile", "mce"] + bool_list = ["global_access", "enhanced_recompile", "mce", "ams"] if xrun_opts_proc ["global_access"]: xrun_opts_proc ["global_access"] = "+access+rcw" @@ -123,7 +133,7 @@ def extract_xrun_opts(self) -> Tuple[Dict[str, str], Dict[str, str]]: xrun_opts_proc ["mce"] = "-mce" else: xrun_opts_proc ["mce"] = "" - + for opt, setting in xrun_opts_proc.items(): if opt not in bool_list and setting is not None: xrun_opts_proc [opt] = f"-{opt} {setting}" @@ -247,7 +257,9 @@ def generate_arg_file(self, [f.write(elem + "\n") for elem in opt_list[1]] f.close() - return arg_path + return arg_path + + # Convenience function invoked when multicore options are needed. def generate_mc_cmd(self) -> str: @@ -306,10 +318,6 @@ def generate_saif_tcl_cmd(self) -> str: saif_args = "" # Process saif options - saif_start_time: Optional[str] = None - saif_end_time: Optional[str] = None - saif_start_trigger_raw: Optional[str] = None - saif_end_trigger_raw: Optional[str] = None if saif_opts["mode"] == "time": saif_start_time = saif_opts["start_time"] saif_end_time = saif_opts["end_time"] @@ -326,16 +334,12 @@ def generate_saif_tcl_cmd(self) -> str: if saif_opts["mode"] is not None: if saif_opts["mode"] == "time": - assert saif_start_time - assert saif_end_time stime = TimeValue(saif_start_time) etime = TimeValue(saif_end_time) saif_args = saif_args + f'dumpsaif -output ucli.saif -overwrite -scope {prefix} -start {stime.value_in_units("ns")}ns -stop{etime.value_in_units("ns")}ns' elif saif_opts["mode"] == "full": saif_args = saif_args + f"dumpsaif -output ucli.saif -overwrite -scope {prefix}" elif saif_opts["mode"] == "trigger_raw": - assert saif_start_trigger_raw - assert saif_end_trigger_raw saif_args = saif_args + f"dumpsaif -output ucli.saif -overwrite -scope {prefix} {saif_start_trigger_raw} {saif_end_trigger_raw}" return saif_args @@ -377,6 +381,336 @@ def generate_sim_tcl(self) -> bool: f.close() return True + def generate_amscf(self) -> bool: + # Open AMS control file template for read. + # Hardcoded path for now + t = open("amscf_template.scs", "r") + + # Create AMS control file (or overwrite if one already exists) for read/write. + # Hardcoded path for now. + f = open(f"./src/amscf.scs", "w+") + + # Get absolute paths for analog models from PDK and schematics from extralibs, respectively. + model_path = self.get_setting("sim.xcelium.anamodels") + models = [modelfile for modelfile in os.scandir(model_path)] + schematic_path = self.get_setting("sim.xcelium.schematics") + schematics = [schematic for schematic in os.scandir(schematic_path)] + + # Get list of paths to individual files within the PDK models (?) and schematic directories, respectively. + #models = [] + #schematics = [] + + # Warnings for missing files. + if (len(schematics) > 0 and len(models) == 0): + self.logger.warning(f"No models found in {model_path} to support analog schematics.") + else: + if (len(models) == 0): + self.logger.warning(f"No models found in {model_path}.") + if (len(schematicpath) == 0): + self.logger.warning(f"No analog schematics found {schematics}.") + + # Get string representation of AMS control file template. + template = t.read() + + # Format modelpaths list as a single string with include statements. + formatted_models = "" + for modelpath in models: + formatted_models += f"include {modelpath}" + + # Format schematicpaths list as a single string with include statements. + formatted_schematics = "" + for schematicpath in schematics: + formatted_schematics += f"include {schematicpath}" + + # Replace empty model deck with formatted string of model paths. + template = re.sub("// model deck\n", "// model deck\n" + formatted_models) + + # Replace empty schematic deck with formatted string of schematic paths. + template = re.sub("// schematic deck\n", "// schematic deck\n" + formatted_schematics) + + # Write filled template to AMS control file. + f.write(template) + + # Close files properly. + t.close() + f.close() + return True + + def retrieve_files(path, exts=[], output_type=str, relative=True): + """ + Returns a list or line-seperated string of all filepaths in a directory and any of its subdirectories. + """ + file_list = [] + extslower = [extension.lower() for extension in exts] + exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] + + for (root, directories, filenames) in os.walk(path): + for filename in filenames: + file_ext = (os.path.splitext(filename)[1]).lower() + rel_root = os.path.relpath(root) + if (relative): + filepath = os.path.join(rel_root, filename) + else: + filepath = f"{os.path.join(root, filename)}" + + if (not exts): + file_list.append(filepath) + elif (file_ext in exts_proc): + file_list.append(filepath) + + if (output_type is str): + return "".join(file_list) + "\n" + elif (output_type is list): + return file_list + else: + return "".join(file_list) + "\n" + + def generate_ams_opts(self): #Hardcoded to example + ams_opts_def = { + "ams": False, + "gen_amscf": False, + "amsconnrules": "" + } + + bool_list = ["ams", "gen_amscf"] + + ### Read in, process, and convert AMS opts into formatted string. + ams_opts = self.get_settings_from_dict(ams_opts_def, key_prefix=self.tool_config_prefix()) + ams_opts_proc = ams_opts.copy() + + # Process special AMS opts + if ams_opts_proc ["ams"]: + ams_opts_proc ["ams"] = "-ams_flex" + else: + ams_opts_proc ["ams"] = "" + + if ams_opts_proc ["gen_amscf"]: + pass + else: + ams_opts_proc ["gen_amscf"] = "" + + if ams_opts_proc ["amsconnrules"]: + ams_opts_proc ["amsconnrules"] = "ConnRules_multpower" + else: + ams_opts_proc ["amsconnrules"] = "" + + # Process non-specified standard AMS opts + for (opt, setting) in ams_opts_proc.items(): + if (opt not in bool_list and setting is not None): + ams_opts_proc [opt] = f"-{opt} {setting}" + + # Convert AMS opt dictionary into formatted string + ams_opt_str = "" + for (opt, setting) in ams_opts_proc.items(): + ams_opt_str += f"{setting}\n" + + ### Read in disciplines file, if it exists. + disciplines = self.get_setting("sim.xcelium.disciplines") + + if (disciplines): + df = open(disciplines, "r") + discpline_str = df.read() + "\n" + df.close() + else: + discpline_str = "" + + ### Read in A/MS schematics, control files, connect libs, and convert them into formatted strings + cwd = os.getcwd() + schematicdir = os.path.join(cwd, self.get_setting("sim.xcelium.schematics")) + connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) + #acf = os.path.join(cwd, self.get_setting("sim.xcelium.acf")) + "\n" + amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + "\n" + + # Retrieve formatted string of schematic files + schematics = self.retrieve_files(schematicdir, exts=[".sp", ".vams"]) + + # Retrieve formatted string of connect files + connectlibs = self.retrieve_files(connectdir, exts=[".vams"]) + + ### Attach additional options + additional_opts = "-spectre_args \"++aps\"\n-messages \n-ieinfo \n" + + ### Create and return combined string + ams_opt_header = "# AMS OPTS:\n" + formatted_opts = ams_opt_header + ams_opt_str + discpline_str + additional_opts + "\n" + + ams_file_header = "# AMS FILES:\n" + formatted_files = ams_file_header + amscf + connectlibs + "\n" + + return formatted_files + formatted_opts + + def attach_opts(self, filepath, attachment): + f = open(filepath, "a+") + f.write(attachment) + f.close + return + + def get_disciplines(self) -> str: + ### Read in disciplines file, if it exists. + disciplines = self.get_setting("sim.xcelium.disciplines") + cwd = os.getcwd() + dpath = os.path.join(cwd, disciplines) + if disciplines: + df = open(dpath, "r") + discipline_opts = df.read() + "\n" + df.close() + return discipline_opts + else: + return "" + + + + if (disciplines): + dpath = os.path.join(cwd, disciplines) + df = open(dpath, "r") + discpline_str = df.read() + disciplines = "\n".split(discpline_str) + discipline_opts = [opt for opt in disciplines if opt != ""] + df.close() + else: + discipline_opts = [] + + if discipline_opts: + discipline_opts_proc = [f"-SETD \"{opt}\"" for opt in discipline_opts] + else: + discipline_opts = [] + + return discipline_opts_proc + + def extract_ams_opts(self, step): + ams_opts = self.get_setting(f"{self.tool_config_prefix()}.ams_opts", []) + cwd = os.getcwd() + match (step): + case ("compile"): + ### Compiles only the files (Digital, V-AMS) + + # Gather compile-time options + + compile_opts = self.extract_ams_compile_opts()[0] + compile_opts_list = []#[setting for setting in compile_opts.values()] + + # Gather AMS files from specified locations. + amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) + + # Gather connect libraries (currently manually added, in future pull from Xcelium install) & connect rules + connectlibs = self.retrieve_files(connectdir, exts=[".vams"], output_type=str) + connrules = os.path.join(cwd, self.get_setting(f"{self.tool_config_prefix()}.amsconnrules")) + ".vams" + + # Add AMS files to compile stage. + #compile_opts_list.append(connectlibs) + #compile_opts_list.append(connrules) + compile_opts_list.append(f"{amscf}") + + #ams_compile_inputs = connectlibs + connrules + #compile_opts_list.append(f"-AMSINPUT\n{ams_compile_inputs}\n") + + return compile_opts_list + + case ("elaborate"): + ### Parses/compiles the source files and elaborates the design. + + # Gather AMS Control File + amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + + # Gather elaboration stage options + elab_opts_proc = self.extract_ams_elab_opts(ams_opts)[0] + elab_opts_str = "" + for (opt, setting) in elab_opts_proc.items(): + elab_opts_str += setting + "\n" + + # Gather and add discipline options, if any. + discpline_opts = self.get_disciplines() + elab_opts_str += discpline_opts + + # Gather AMS Files from specified locations. + amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) + + # Gather connect libraries and rules. + connectlibs = self.retrieve_files(connectdir, exts=[".vams"], output_type=str) + connrules = os.path.join(cwd, self.get_setting(f"{self.tool_config_prefix()}.amsconnrules")) + ".vams" + + # Add AMS files to elaboration stage as AMS inputs. + elab_opts_str += f"\n{connectlibs}\n" + elab_opts_str += f"{connrules}\n" + + # Add AMS Control File. + #elab_opts_str += f"{amscf}\n" + + return elab_opts_str + case ("sim"): + return self.extract_ams_sim_opts(ams_opts) + return + + def extract_ams_compile_opts(self) -> Tuple[Dict[str, str], Dict[str, str]]: + ams_compile_opts_def = { + "genamscf": False + } + + ams_compile_opts = self.get_settings_from_dict(ams_compile_opts_def, key_prefix=self.tool_config_prefix()) + ams_compile_opts_proc = ams_compile_opts.copy() + bool_list = ["genamscf"] + if ams_compile_opts_proc ["genamscf"]: + pass #In future, generate AMSCF from template and place it in specified location + else: + pass + + for opt, setting in ams_compile_opts_proc.items(): + if opt not in bool_list and setting is not None: + ams_compile_opts_proc [opt] = f"-{opt} {setting}" + + return ams_compile_opts_proc, ams_compile_opts + + def extract_ams_elab_opts(self, ams_opts) -> Tuple[Dict[str, str], Dict[str, str]]: + ams_elab_opts_def = { + "ams_flex": False, + "amsconnrules": None, + "ieinfo": False + } + + bool_list = ["ams_flex", "ieinfo"] + + ams_elab_opts = {} + for opt in ams_opts: + ams_elab_opts [opt] = opt + ams_elab_opts_proc = ams_elab_opts.copy() + + for opt in ams_elab_opts_proc: + if opt in bool_list: + ams_elab_opts_proc [opt] = f"-{opt}" + + # Specific settings for AMS connection rules + amsconnrules = self.get_setting(f"{self.tool_config_prefix()}.amsconnrules") + amsconnrules_basepath, amsconnrules_name = os.path.split(amsconnrules) + if amsconnrules: + ams_elab_opts_proc ["amsconnrules"] = f"-AMSCONNRULES\n{amsconnrules_name}" + elif ("amsconnrules" not in ams_elab_opts): + ams_elab_opts_proc.pop("amsconnrules") + + # Extra args + + ams_elab_opts_proc ["spectre_args"] = f"-SPECTRE_ARGS \"++aps\"" + #ams_elab_opts_proc ["AMSINPUT"] = f"-AMSINPUT {amscf}" + + amscf = self.get_setting(f"{self.tool_config_prefix()}.amscf") + ams_elab_opts_proc ["analogcontrol"] = f"-ANALOGCONTROL {amscf}" + #ams_elab_opts_proc ["AMS_ELAB"] = f"-AMS_ELAB" + + return ams_elab_opts_proc, ams_elab_opts + + def extract_ams_sim_opts(self, ams_opts): + cwd = os.getcwd() + amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + sim_opts = [f"-ANALOGCONTROL {amscf}", f"-AMS_FLEX", f"-SPECTRE_ARGS \"++aps\""] + sim_opts_str = "" + + sim_opts_str += f"-ANALOGCONTROL {amscf}" + sim_opts_str += f"-AMS_FLEX" + sim_opts_str += f"-SPECTRE_ARGS \"++aps\"" + + return sim_opts + def compile_xrun(self) -> bool: if not os.path.isfile(self.xcelium_bin): @@ -388,7 +722,7 @@ def compile_xrun(self) -> bool: # Gather complation-only options xrun_opts = self.extract_xrun_opts()[1] - compile_opts = self.get_setting(f"{self.tool_config_prefix}.compile_opts", []) + compile_opts = self.get_setting(f"{self.tool_config_prefix()}.compile_opts", []) compile_opts.append("-logfile xrun_compile.log") if xrun_opts["mce"]: compile_opts.append(self.generate_mc_cmd()) compile_opts = ('COMPILE', compile_opts) @@ -398,6 +732,11 @@ def compile_xrun(self) -> bool: args.append(f"-compile -f {arg_file_path}") self.update_submit_options() + + ### If AMS enabled, submit options but do not run compile sub-step. + if self.get_setting(f"{self.tool_config_prefix()}.ams"): + return True + self.run_executable(args, cwd=self.run_dir) HammerVLSILogging.enable_colour = True HammerVLSILogging.enable_tag = True @@ -406,7 +745,7 @@ def compile_xrun(self) -> bool: def elaborate_xrun(self) -> bool: xrun_opts = self.extract_xrun_opts()[1] sim_opts = self.extract_sim_opts()[1] - elab_opts = self.get_setting(f"{self.tool_config_prefix}.elab_opts", []) + elab_opts = self.get_setting(f"{self.tool_config_prefix()}.elab_opts", []) elab_opts.append("-logfile xrun_elab.log") elab_opts.append("-glsperf") elab_opts.append("-genafile access.txt") @@ -427,27 +766,44 @@ def elaborate_xrun(self) -> bool: if xrun_opts["mce"]: elab_opts.append(self.generate_mc_cmd()) elab_opts = ('ELABORATION', elab_opts) + + arg_file_path = self.generate_arg_file("xrun_elab.arg", "HAMMER-GEN XRUN ELAB ARG FILE", [elab_opts]) args =[self.xcelium_bin] args.append(f"-elaborate -f {arg_file_path}") - + self.update_submit_options() + ### If AMS enabled, submit options but do not run elaborate sub-step. + if self.get_setting(f"{self.tool_config_prefix()}.ams"): + return True + """if self.get_setting(f"{self.tool_config_prefix()}.ams"): + ams_elab_opts = self.extract_ams_opts("elaborate") + self.attach_opts(arg_file_path, ams_elab_opts) + #Add all necessary AMS specific commands, remove other commands as necessary""" + self.run_executable(args, cwd=self.run_dir) return True def sim_xrun(self) -> bool: sim_opts = self.extract_sim_opts()[1] + ams_opts = self.extract_ams_opts("sim") sim_cmd_opts = self.get_setting(f"{self.sim_input_prefix}.options", []) sim_opts_removal = ["tb_name", "input_files", "incdir"] xrun_opts_removal = ["enhanced_recompile", "mce"] + ams_opts_removal = [] + + #if (self.get_setting(f"sim.xcelium.ams")): sim_cmd_opts.extend(ams_opts) + sim_cmd_opts = ('SIMULATION', sim_cmd_opts) if not sim_opts["execute_sim"]: self.logger.warning("Not running any simulations because sim.inputs.execute_sim is unset.") return True - arg_file_path = self.generate_arg_file("xrun_sim.arg", "HAMMER-GEN XRUN SIM ARG FILE", [sim_cmd_opts], + + + arg_file_path = self.generate_arg_file("xrun_sim.arg", "HAMMER-GEN XRUN SIM ARG FILE", [sim_cmd_opts, 'SIMULATION'], sim_opt_removal = sim_opts_removal, xrun_opt_removal = xrun_opts_removal) args =[self.xcelium_bin] @@ -455,7 +811,370 @@ def sim_xrun(self) -> bool: self.generate_sim_tcl() self.update_submit_options() + + ### If AMS enabled, submit options and procede to AMS single-step function + if self.get_setting(f"{self.tool_config_prefix()}.ams"): + self.run_mxh_pseudo_three_step() + return True + + self.run_executable(args, cwd=self.run_dir) return True + + def sift_exts(list, exts): + """ + Returns a list of filepaths whose filenames contain any of the extensions in the specified extension list + """ + exts_lower = [extension.lower() for extension in exts] + exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in exts_lower] + + list_proc = [os.path.splitext(path.lower()) for path in list] + + sifted = [f"{name}{ext}" for (name, ext) in list_proc if ext in exts_proc] + + return sifted + + def retrieve_files(path, exts=[], output_type=str, relative=True): + """ + Returns a list or line-seperated string of all filepaths in a directory and any of its subdirectories. + """ + file_list = [] + extslower = [extension.lower() for extension in exts] + exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] + + for (root, directories, filenames) in os.walk(path): + for filename in filenames: + file_ext = (os.path.splitext(filename)[1]).lower() + rel_root = os.path.relpath(root) + if (relative): + filepath = os.path.join(rel_root, filename) + else: + filepath = f"{os.path.join(root, filename)}" + + if (not exts): + file_list.append(filepath) + elif (file_ext in exts_proc): + file_list.append(filepath) + + if (output_type is str): + return "".join(file_list) + "\n" + elif (output_type is list): + return file_list + else: + return "".join(file_list) + "\n" + + def vlog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + """ + Returns a formatted string of all verilog/VHDL files in the source + """ + vlog = "" + if (collect): + sourcepath = os.path.join(os.getcwd(), sourcedir) + vlog_list = xcelium.retrieve_files(sourcepath, [".v", ".vhdl"], list) + else: + vlog_list = xcelium.sift_exts(sourcelist, [".v"]) + + if (blacklist and vlog_list): + for pathname in blacklist: + if pathname in vlog_list: + vlog_list.remove(pathname) + + if vlog_list: + vlog = " \\\n".join(vlog_list) + " \\\n" + + return f"{vlog}" + + def vams_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + """ + Returns a formatted string of all V-AMS files in the source + """ + vams = "" + if (collect): + sourcepath = os.path.join(os.getcwd(), sourcedir) + vams_list = xcelium.retrieve_files(sourcepath, [".vams"], list) + else: + vams_list = xcelium.sift_exts(sourcelist, [".vams"]) + + if (blacklist and vams_list): + for pathname in blacklist: + if pathname in vams_list: + vams_list.remove(pathname) + + if vams_list: + vams = " \\\n".join(vams_list) + " \\\n" + + return f"{vams}" + + def analog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + """ + Returns a formatted string of all analog (.scs) files in the source + """ + control = "" + if (collect): + sourcepath = os.path.join(os.getcwd(), sourcedir) + control_list = xcelium.retrieve_files(sourcepath, [".scs"], list) + else: + control_list = xcelium.sift_exts(sourcelist, [".scs"]) + + if (blacklist and control_list): + for pathname in blacklist: + if pathname in control_list: + control_list.remove(pathname) + + if control_list: + control = " \\\n".join(control_list) + " \\\n" + + return f"{control}" + + def discipline_collector(discipline_filename) -> str: + """ + Returns a formatted string of all disciplines from the disciplines.txt file + """ + ### Read in disciplines file, if it exists. + disciplines = os.path.join(os.getcwd(), discipline_filename) + dpath = os.path.join(os.getcwd(), disciplines) + if disciplines: + df = open(dpath, "r") + discipline_opts = df.read() + disciplines_formatted = re.sub("\n", " \\\n", discipline_opts) + " \\" + df.close() + return disciplines_formatted + else: + return "" + + def option_preparer(self, opts, pseudo_step=True) -> str: + """ + Returns a formatted string of all provided AMS options and their arguments + """ + bool_list = ["ams", "disciplines", "gen_amscf"] + opts_proc = opts.copy() + + if not opts: + return "" + + if opts ["ams"] is True: + opts_proc ["ams"] = "-ams_flex" + " \\" + else: + opts_proc ["ams"] = "" + + if opts ["gen_amscf"] is True: + self.generate_amscf(self.get_setting("sim.xcelium.amscf_template"), self.get_setting("sim.xcelium.amscf")) # Expect names, not filepaths + opts_proc ["gen_amscf"] = "" + else: + opts_proc ["gen_amscf"] = "" + + if opts ["disciplines"]: + opts_proc ["disciplines"] = xcelium.discipline_collector(opts["disciplines"]) + + if opts ["amsconnrules"]: + opts_proc ["amsconnrules"] = opts["amsconnrules"] + + if (pseudo_step): + digital_opts = self.option_extractor(["xrun_compile.arg", "xrun_elab.arg", "xrun_sim.arg"]) + digital_opts.update(opts_proc) # Should any keys match, AMS arguments take precedence + opts_proc = digital_opts + + #Fixed Extra Opts + opts_proc ["timescale"] = self.get_setting("sim.inputs.timescale") + opts_proc ["input"] = "probe.tcl" + opts_proc ["access"] = "+rwc" + opts_proc ["messages"] = "" + opts_proc ["spectre_args"] = "\"++aps\"" + opts_proc ["ieinfo"] = "" + + opts_proc = {opt:setting for (opt, setting) in opts_proc.items() if opt not in bool_list and setting is not None} + + opts_len = len(opts_proc) - 1 + for (n, (opt, setting)) in enumerate(opts_proc.items()): + if (n == opts_len): + if (setting == ""): + opts_proc [opt] = f"-{opt}" + else: + opts_proc [opt] = f"-{opt} {setting}" + else: + if (setting == ""): + opts_proc [opt] = f"-{opt} \\" + else: + opts_proc [opt] = f"-{opt} {setting} \\" + + + opts_rev = {k: v for k, v in opts_proc.items() if v} + + opts_proc_str = "\n".join(opts_rev.values()) + return f"{opts_proc_str}" + + def generate_amscf(self, template_filename, amscontrol_filename) -> bool: + """ + Creates an AMS control file based on templated format with available analog models & schematics + """ + # Open AMS control file template for read. + template_path = os.path.join(os.getcwd(), template_filename) + t = open(template_path, "r") + + # Create AMS control file (or overwrite if one already exists) for read/write. + amscontrol_path = os.path.join(os.getcwd(), amscontrol_filename) + f = open(amscontrol_path, "w+") + + # Get normalized, absolute paths for analog model files + model_path = os.path.join(os.getcwd(), self.get_setting("sim.xcelium.anamodels")) + models = [os.path.normpath(modelfile.path) for modelfile in os.scandir(model_path)] + + # Get normalized, absolute paths for analog schematic files + schematic_path = os.path.join(os.getcwd(), self.get_setting("sim.xcelium.schematics")) + schematics = [os.path.normpath(schematic.path) for schematic in os.scandir(schematic_path)] + + # Warnings for missing files. + if (len(schematics) > 0 and len(models) == 0): + logging.warning(f"No models found in {model_path} to support analog schematics.") + else: + if (len(models) == 0): + logging.warning(f"No models found in {model_path}.") + if (len(schematic_path) == 0): + logging.warning(f"No analog schematics found {schematics}.") + + # Get string representation of AMS control file template. + template = t.read() + + # Format model_paths list as a single string with include statements. + formatted_models = "" + for modelpath in models: + formatted_models += f"include {modelpath!r}\n" + # Format schematic_paths list as a single string with include statements. + formatted_schematics = "" + for schematicpath in schematics: + formatted_schematics += f"include {schematicpath!r}\n" + + # Replace empty model deck with formatted string of model paths. + model_template = re.sub("// model deck\n", "// model deck\n" + formatted_models, template) + + # Replace empty schematic deck with formatted string of schematic paths. + schematic_template = re.sub("// schematic deck\n", "// schematic deck\n" + formatted_schematics, model_template) + + # Write filled template to AMS control file. + f.write(schematic_template) + + # Close files properly. + t.close() + f.close() + return True + + def option_extractor(self, argfile_names=[]): + if not argfile_names: + return {} + + opts = {} + + # Extract the options from each argfile listed, ignoring duplicate opts and file inclusions + for filename in argfile_names: + path = os.path.join(self.run_dir, filename) + file_opts = {} + f = open(path, "r") + + for line in f: + if (line[0] == "-"): + split_line = line.split(sep=None, maxsplit=2) + if (len(split_line) > 1): + opt_key, opt_arg = split_line[0].strip("- "), split_line[1].lstrip("\n") + else: + opt_key, opt_arg = split_line[0].strip("- "), "" + + file_opts[opt_key] = opt_arg + + opts.update(file_opts) + f.close() + + return opts + + def scriptwriter(self, collect=False, sourcedir="", blacklist=[], sourcelist=[], options={"ams": False, "disciplines": "", "amsconnrules": "", "gen_amscf": False}): + """ + Writes all prepared files and arguments to the run_mxh shell script + """ + runpath = os.path.join(self.run_dir, "run_mxh") + + f = open(runpath, "w+") + + # Write Shebang + xrun clean + f.write("#!/bin/csh -f\n#\nxrun -clean \\\n") + + # Write Digital Files + f.write(xcelium.vlog_preparer(collect, sourcelist, sourcedir, blacklist)) + f.write(xcelium.vams_preparer(collect, sourcelist, sourcedir, blacklist)) + + # Write Analog Files + f.write(xcelium.analog_preparer(collect, sourcelist, sourcedir, blacklist)) + + # Write Options + f.write(self.option_preparer(options)) + + f.close() + return + + def run_mxh(self) -> bool: + if not os.path.isfile(self.xcelium_bin): + self.logger.error(f"Xcelium (xrun) binary not found at {self.xcelium_bin}.") + return False + + if not self.check_input_files(self.xcelium_ext): + return False + + digital_files = self.get_setting("sim.inputs.input_files") + acf = self.get_setting("sim.xcelium.acf") + amscf = self.get_setting("sim.xcelium.amscf") + connectlibs = self.get_setting("sim.xcelium.connectlibs") + anamodels_dir = self.get_setting("sim.xcelium.schematics") + + connrules = self.get_setting("sim.xcelium.amsconnrules") + ams_opts = self.get_setting("sim.xcelium.ams_opts") + + source = digital_files + [acf, amscf, connectlibs] + + ams_opts_dict = { + "ams": self.get_setting("sim.xcelium.ams"), + "disciplines": self.get_setting("sim.xcelium.disciplines"), + "amsconnrules": self.get_setting("sim.xcelium.amsconnrules"), + "gen_amscf": self.get_setting("sim.xcelium.genamscf") + } + + self.scriptwriter(collect=True, sourcedir="src/", blacklist=["src/amscf_template.scs", "src/ams_control/acf.scs"], options=ams_opts_dict) + + self.update_submit_options() + self.run_executable(["./run_mxh"], cwd=self.run_dir) + return True + + def run_mxh_pseudo_three_step(self) -> bool: + if not os.path.isfile(self.xcelium_bin): + self.logger.error(f"Xcelium (xrun) binary not found at {self.xcelium_bin}.") + return False + + if not self.check_input_files(self.xcelium_ext): + return False + + digital_files = self.get_setting("sim.inputs.input_files") + acf = self.get_setting("sim.xcelium.acf") + amscf = self.get_setting("sim.xcelium.amscf") + connectlibs = self.get_setting("sim.xcelium.connectlibs") + anamodels_dir = self.get_setting("sim.xcelium.schematics") + + connrules = self.get_setting("sim.xcelium.amsconnrules") + ams_opts = self.get_setting("sim.xcelium.ams_opts") + + source = digital_files + [acf, amscf, connectlibs] + + ams_opts_dict = { + "ams": self.get_setting("sim.xcelium.ams"), + "disciplines": self.get_setting("sim.xcelium.disciplines"), + "amsconnrules": self.get_setting("sim.xcelium.amsconnrules"), + "gen_amscf": self.get_setting("sim.xcelium.genamscf") + } + + self.scriptwriter(collect=True, sourcedir="src/", blacklist=["src/amscf_template.scs", "src/ams_control/acf.scs"], options=ams_opts_dict) + + # Extract digital-only options from compile, elab, and sim argfiles + combined_opts = self.option_extractor(["xrun_compile.arg", "xrun_elab.arg", "xrun_sim.arg"]) + + + self.update_submit_options() + self.run_executable(["./run_mxh"], cwd=self.run_dir) + return True tool = xcelium diff --git a/hammer/sim/xcelium/defaults.yml b/hammer/sim/xcelium/defaults.yml index 980bbe29c..9fbbf4daa 100644 --- a/hammer/sim/xcelium/defaults.yml +++ b/hammer/sim/xcelium/defaults.yml @@ -1,11 +1,18 @@ sim.xcelium: - # Tool version (e.g., "XCELIUM2103") + # Tool version (e.g., "XCELIUM2103") version: "XCELIUM2103" + # Spectre version (e.g., "SPECTRE211") + spectreversion: "SPECTRE211" + # Path to xcelium binary. xcelium_bin: "${cadence.cadence_home}/XCELIUM/${sim.xcelium.version}/tools/xcelium/bin/64bit/xrun" xcelium_bin_meta: lazysubst + # Path to spectre binary. + spectre_bin: "${cadence.cadence_home}/SPECTRE/${sim.xcelium.spectreversion}/tools/spectre/bin/64bit/spectre" + spectre_bin_meta: lazysubst + # Path to xmsimrc_def file. xmsimrc_def: "${cadence.cadence_home}/XCELIUM/${sim.xcelium.version}/tools/xcelium/files/xmsimrc" xmsimrc_def_meta: lazysubst @@ -33,4 +40,26 @@ sim.xcelium: compile_opts: null # Opts to access elaboration step in xcelium. elab_opts: null - + # If true, run as mixed-signal (AMS) simulation. + # By default it should be FALSE as requirements and inputs are different to digital-only simulation. + ams: False + # Specifies location of analog control file. + acf: null + # Specifies location of ams control file. + amscf: null + # If true, create a partially populated template of the AMS control file. + gen_amscf: False + # To be removed in future. + anamodels: null + # To be removed in future. + schematics: null + # List of strings specifying ams options. + ams_opts: null + # Path to .txt file containing formatted disciplines. + disciplines: null + # Path to connect library files (cross-power domain libs). + connectlibs: null + # Path to file specifying cross-power domain connection rules (if applicable). + amsconnrules: null + # Path to ams control file template. + amscf_template: null diff --git a/hammer/sim/xcelium/defaults_types.yml b/hammer/sim/xcelium/defaults_types.yml index e793c0b11..9158d1372 100644 --- a/hammer/sim/xcelium/defaults_types.yml +++ b/hammer/sim/xcelium/defaults_types.yml @@ -1,6 +1,10 @@ sim.xcelium: # Tool version (e.g., "XCELIUM2103") version: str + # Spectre version (e.g. "SPECTRE211") + spectreversion: Optional[str] + # Path to spectre binary. + spectre_bin: Optional[str] # Path to xcelium binary. xcelium_bin: str # Path to xmsimrc_def file. @@ -19,6 +23,29 @@ sim.xcelium: global_access: bool # If true, enable multicore support. mce: bool + # If true, enable AMS sims. + ams: bool + # Path to analog control file. + acf: Optional[str] + # Path to ams control file. + amscf: Optional[str] + # If true, create a partially populated template of the AMS control file. + gen_amscf: Optional[bool] + # Path to analog models directory. + # To be removed in future, pulled instead from PDK library models + anamodels: Optional[str] + # Path to analog schematics directory. + # To be removed in future, pulled from EXTRA_LIBS + schematics: Optional[str] + # Path to .txt file containing formatted disciplines. + disciplines: Optional[str] + # Path to connect library files (cross-power domain libs). + connectlibs: Optional[str] + # Path to file specifying cross-power domain connection rules (if applicable). + amsconnrules: Optional[str] + # Path to ams control file template. + amscf_template: Optional[str] compile_opts: Optional[list[str]] - elab_opts: Optional[list[str]] \ No newline at end of file + elab_opts: Optional[list[str]] + ams_opts: Optional[list[str]] \ No newline at end of file From aebf3dca791d7850004468a20b30b2b3753a8913 Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Wed, 22 May 2024 15:29:35 -0700 Subject: [PATCH 2/9] Added documentation for this version --- e2e/Makefile | 13 +-- e2e/mxhammersetupguide.md | 208 +++++++++++++++++++++++++++++++++ e2e/src/disciplines.txt | 0 hammer/sim/xcelium/__init__.py | 6 +- 4 files changed, 218 insertions(+), 9 deletions(-) create mode 100644 e2e/mxhammersetupguide.md create mode 100644 e2e/src/disciplines.txt diff --git a/e2e/Makefile b/e2e/Makefile index 00c370bac..2d9adb0c5 100644 --- a/e2e/Makefile +++ b/e2e/Makefile @@ -3,9 +3,9 @@ vlsi_dir=$(abspath .) # minimal flow configuration variables -design ?= pass +design ?= level_shifter pdk ?= sky130 -tools ?= nop +tools ?= cm env ?= bwrc extra ?= # extra configs @@ -22,9 +22,9 @@ TOOLS_CONF ?= configs-tool/$(tools).yml # design-specific overrides of default configs DESIGN_CONF ?= configs-design/$(design)/common.yml DESIGN_PDK_CONF ?= configs-design/$(design)/$(pdk).yml -SIM_CONF ?= $(if $(findstring -rtl,$(MAKECMDGOALS)), configs-design/$(design)/sim-rtl.yml, \ - $(if $(findstring -syn,$(MAKECMDGOALS)), configs-design/$(design)/sim-syn.yml, \ - $(if $(findstring -par,$(MAKECMDGOALS)), configs-design/$(design)/sim-par.yml, ))) +SIM_CONF ?= $(if $(findstring rtl,$(MAKECMDGOALS)), configs-design/$(design)/sim-rtl.yml, \ + $(if $(findstring syn,$(MAKECMDGOALS)), configs-design/$(design)/sim-syn.yml, \ + $(if $(findstring par,$(MAKECMDGOALS)), configs-design/$(design)/sim-par.yml, ))) POWER_CONF ?= $(if $(findstring power-rtl,$(MAKECMDGOALS)), configs-design/$(design)/power-rtl-$(pdk).yml, \ $(if $(findstring power-syn,$(MAKECMDGOALS)), configs-design/$(design)/power-syn-$(pdk).yml, \ $(if $(findstring power-par,$(MAKECMDGOALS)), configs-design/$(design)/power-par-$(pdk).yml, ))) @@ -43,6 +43,3 @@ $(HAMMER_D_MK): hammer-vlsi --obj_dir $(OBJ_DIR) -e $(ENV_YML) $(HAMMER_EXTRA_ARGS) build -include $(HAMMER_D_MK) - -clean: - rm -rf $(OBJ_DIR) hammer-vlsi-*.log diff --git a/e2e/mxhammersetupguide.md b/e2e/mxhammersetupguide.md new file mode 100644 index 000000000..b0831e27a --- /dev/null +++ b/e2e/mxhammersetupguide.md @@ -0,0 +1,208 @@ +# Provisional MXHammer Setup Guide (E2E) + +## 1. Hammer Setup +### 1.1 Setup the Hammer environment according to the steps under 1.2.2. Developer Setup in the Hammer documentation, found [here](https://hammer-vlsi.readthedocs.io/en/stable/Hammer-Basics/Hammer-Setup.html#developer-setup) or below. + +#### 1.2.2. Developer Setup +##### 1. Clone Hammer with `git` +``` +git clone https://github.com/ucb-bar/hammer.git +cd hammer +``` +##### 2. [Install poetry](https://python-poetry.org/docs/master/) to manage the development virtualenv and dependencies +``` +curl -sSL https://install.python-poetry.org | python3 - +``` +##### 3. Create a poetry-managed virtualenv using the dependencies from `pyproject.toml` +``` +# create the virtualenv inside the project folder (in .venv) +poetry config virtualenvs.in-project true +poetry install +``` +##### 4. Activate the virtualenv. Within the virtualenv, Hammer is installed and you can access its scripts defined in `pyproject.toml` (in `[tool.poetry.scripts]`) +``` +poetry shell +hammer-vlsi -h +``` +### 1.2 Set simulator to Xcelium +Under `hammer/e2e/configs-tool/cm.yml`, find the simulator tool key: `vlsi.core.sim_tool` and change the value from VCS to Xcelium. This should like the following when done: `vlsi.core.sim_tool: "hammer.sim.xcelium"`. + +### 1.3 Setup design configuration file +Create a folder under `hammer/e2e/configs-design` to hold the configuration files for your design. For the AMS simulation, at minimum, this folder should contain a `common.yml`, `sim-rtl.yml`, and a process configuration file such as `asap7.yml` or `sky130.yml`. + + +## 2. MXHammer Setup + +### 2.1 Replace standard Xcelium plugin files with their MXHammer variants +The files for the MXHammer variant of Hammer can be found in the `ams_experimental` branch of [Hammer](https://github.com/ucb-bar/hammer.git). If you have the correct files, the following comment should appear within the `__init__.py`: `# MXHammer version` . + +### 2.2 Simulator Setup +#### 2.2.1 General Simulator Settings +The first portion of your `sim-rtl.yml` should contain general simulator settings, such as the top module, testbench name/DUT, etc. **The level setting should be set to "rtl".** It should look something like the following when filled: +``` +sim.inputs: + top_module: "modulename" + tb_name: "testbench" + tb_dut: "module0" + level: "rtl" + input_files: ["src/digital/inv.v", "src/digital/testbench.vams"] + timescale: "1ns/100ps" + waveform.type: null + waveform.dump_name: "wave" + waveform.compression: False + waveform.probe_paths: "-all" + waveform.tcl_opts: null +``` +All Verilog/VHDL and Verilog-AMS files should be included within `input_files`, except for connect module library files, and AMS connection rule files. These are explained later below. + +#### 2.2.2 Xcelium Simulator Settings +The second portion of your `sim-rtl.yml` should contain Xcelium-specific settings for digital and optionally AMS simulation. In order to see what each key expects as a value, please see `defaults.yml` within `hammer/hammer/sim/xcelium`. + +However, particular elements may be unfamiliar, such as `disciplines`, `connectlibs`, and `connrules`. + +##### Disciplines +These are a set of definitions and properties for a specific type of system. It is a combination of an analog **potential** (ex. voltage) and ***flow* nature** (ex. current). **Natures** are declarations that define a collection of **attributes**. The defined nature is then used to define disciplines and other natures. + +##### Disciplines for specific scopes +We can use `-setdiscipline` or `-setd` option to specify to the elaborator which disciplines to apply to domain-less (think voltage for analog, or high/low for digital) nets in a specified design scope. + +The following is the syntax for specifying disciplines: +``` +-setd "LIB-lib_name- discipline_name" +-setd "CELL-lib_name.cell_name:view_name- discipline_name" +-setd "CELLTERM-cell_name.port_name- discipline_name" +-setd "INST-hierarchical_instance_name- discipline_name" +-setd "INSTTERM-hierarchical_instance_name- discipline_name" +``` +Example disciplines for a 3.3V Buffer: +``` +-SETD "INSTTERM-testbench.vlog_buf.I1.in- logic33V" +-SETD "INSTTERM-testbench.vlog_buf.in_33v- logic33V" +-SETD "INSTTERM-testbench.vlog_buf.out_33v- logic33V" +-SETD "INSTTERM-testbench.vlog_buf.I2.out- logic33V" +``` + +`-setdisciple` \ `-setd` can also be used to allow default default disciplines to be applied to different blocks. +``` +-discipline logic 18V + -setd "inst-top.I1.I2- logic_33V" + -setd "inst-top.I1.I3- logic_5V" + -setd "inst-top.I1.I4- electrical" + -setd "net-top.I1.out- logic_33V" +``` +##### Discipline specification for MXHammer +Place all disciplines, formatted in the manner shown above, within a .txt file and provide the path to the file relative to the working directory to the `sim.xcelium.disciplines` key. + +##### Connect Modules (CMs) / Interface Elements (IEs) +Connect modules or interface elements slot between signal domains like electrical, logic, and real. They are inserted *automatically* or *manually* by using connect or **ie** statements, which contain the code required to translate and propagate signals between **nets** that have different **disciplines** connected through a port. + +##### Connect Libraries +The connect libraries are a set of files which define how signal domains should be translated between various **disciplines**. As of now they are not included within the public-facing version of MXHammer. + +##### Connect Rules +Defines a set of **connect modules** to be used in a design and notifies the simulator about which CMs to use and which is chosen from the built-in set provided in the software installation or from a user-defined set + +### 2.3 A/MS Control Files +For an AMS simulation, we need to specify the analog signals, models used in the design, which analog simulation we wish to perform, the analog design itself, and how we want to connect that design to external blocks through and **AMSD** block. + +#### 2.3.1 AMSD Control Files +The AMS control file (**AMSCF**) specifies: + - The simulator language (should be spectre in most cases) + - Global signals (such as `0`/`vss!` and `vdd!`) + - The models to be used in the simulation of the design, an **analog control file** (if the information within the **ACF** is not included within an **AMSD** block within the **AMSCF**). ***ALL*** model files used within the design **must** be included (section of the model the particular block resides in is usually fine). + - **NOTE:** When generating the netlist for your analog design, do so as **CDL**. This can be done from **ICADV** GUI as File -> Export -> CDL. Alternatively you can use the Virtuoso CDL out interface. + - The design(s), whose file(s) must be included, like the model files. + - **AMSD** Block(s) + +##### AMSD Blocks +These 'AMS Designer' blocks can contain any design partitioning, disciplines, and connect modules. Where 'partitioning' includes setting the use of spice, portmaps for subcircuits, bus delimiters, and setting `autobus` to `yes` or `no`. Disciplines include connecting the power supply voltage when connecting from an analog voltage to logic. + +Ex 1: `ie vsup=1.8 discipline=logic18V` tells the elaborator that 1.8V is a logic 1 for that the analog real output + +Ex 2: A generic, mostly unfilled AMSD block might look like the following: +``` +amsd{ + +// set the connect rule power supply + ie vsup=? discipline=logic?V + +// mix analog and digital +portmap subckt=ckt_name autobus=yes busdelim="[] <>" +config cell=ckt_name use=spice +} +``` + +##### AMSCF Example for a skywater 130nm level-shifter +In the following example, notice `autobus` is set to `yes`, this specifies that Xcelium will automatically generate CMs/IEs between nets with different domains. +``` +simulator lang=spectre + +// global signals +global 0 vdd! + +//model deck - Skywater +include "/tools/C/miguel4141/mxhammer/mx-project/bag-sky130/bag3_skywater130_workspace/skywater130/workspace_setup/PDK/MODELS/SPECTRE/s8phirs_10r/Models/design_wrapper.lib.scs" section=tt_fet +include "/tools/C/miguel4141/mxhammer/mx-project/bag-sky130/bag3_skywater130_workspace/skywater130/workspace_setup/PDK/MODELS/SPECTRE/s8phirs_10r/Models/design_wrapper.lib.scs" section=tt_cell +include "/tools/C/miguel4141/mxhammer/mx-project/bag-sky130/bag3_skywater130_workspace/skywater130/workspace_setup/PDK/MODELS/SPECTRE/s8phirs_10r/Models/design_wrapper.lib.scs" section=tt_parRC +include "/tools/C/miguel4141/mxhammer/mx-project/bag-sky130/bag3_skywater130_workspace/skywater130/workspace_setup/PDK/MODELS/SPECTRE/s8phirs_10r/Models/design_wrapper.lib.scs" section=tt_rc +include "/tools/C/miguel4141/mxhammer/mx-project/bag-sky130/bag3_skywater130_workspace/skywater130/workspace_setup/PDK/MODELS/SPECTRE/s8phirs_10r/Models/design_wrapper.lib.scs" section=npn_t + +//Analog Control File +include "acf.scs" + +//Cadence Level Shifter +//include "src/analog/level_shifter.sp" + +//Skywater Level Shifter +include "src/analog/sky130_level_shifter.sp" + +// ams configuration options +amsd{ + +// set the connect rule power supply + ie vsup=1.8 discipline=logic18V + +// mix analog and digital +portmap subckt=sky130_level_shifter autobus=yes busdelim="[] <>" +config cell=sky130_level_shifter use=spice +} + +``` + +#### 2.3.2 Analog Control Files +The analog control file (**ACF**) contains the analog simulation type and settings which the AMS simulation will use. They are specified in the spectre language. + +Ex: +``` +*********************************** +simulator lang=spectre +*********************************** + +*--------------------------------------------------------* +* Spectre Fast APS Analysis Options/Spectre X options +*--------------------------------------------------------* +tran1 tran start=1ns stop=1us method=trap + +*--------------------------------------------------------* +* Spectre Fast APS Simulator Options options +*--------------------------------------------------------* +save * sigtype=node depth=4 +``` + +## 2.4 Running MXHammer +To run the AMS simulation, ensure that the `ams` key in `sim-rtl.yml` is set to `True` and you have all other relevant files and values to the keys set. For any keys whose value is a path to a file or directory which is not needed for your simulation (such as `connectlibs` or `connectrules`), set that value to `None` in `sim-rtl.yml`. Additionally, some additional options are currently hardcoded, so please be sure to use a `.tcl` file to specify which signals you would like to save from the AMS simulation, name that file `probe.tcl`, and put it in `e2e/src/`. If you are unfamiliar with how to put together a `.tcl` probe, a guide can be found [here](https://community.cadence.com/cadence_blogs_8/b/cic/posts/start-your-engines-use-tcl-commands-to-save-signals-more-efficiently-in-mixed-signal-simulations). + +### 2.4.1 Xcelium + MXHammer Mechanics +Xcelium works in one of two primary methodologies, **three-step** or **single-step**. In the three-step methodology, the user (or Hammer) steps through the compilation, elaboration, and simulation stages. In the single-step methodology, the user provides all necessary information up front and Xcelium internally steps through all three stages. The base Xcelium plugin operates off of the three-step methodology. + +MXHammer operates on a pseudo-three-step methodology which takes in the arguments generated by the three-step process of the base digital-only Xcelium plugin and the specified AMS arguments to create a shell script for a single run. + +### 2.4.2 Starting the MXHammer run +To begin the MXHammer, simply go to your hammer folder, initiate the poetry environment, then go to the `e2e` folder. Once there, type `make build` into the command line to generate the `hammer.d` file and then type `make sim-rtl` to begin the run. + + +### 2.4.3 Initiating the AMS Simulation +Currently, there are issues with the step of initiating the run automatically, so you will likely need to do so yourself. All you need to do so is to go to the build directory under `e2e`, under which you fill find the build for your design, under which you can find `sim-rtl-rundir` (the full path will look something like `hammer/e2e/build-node-env/design_name/sim-rtl-rundir`). Once you are there, ensure the `run_mxh` shell script has the correct permissions and then simply type `./run_mxh` into the command line. + + +# For any questions, email Miguel at miguel4141@berkeley.edu diff --git a/e2e/src/disciplines.txt b/e2e/src/disciplines.txt new file mode 100644 index 000000000..e69de29bb diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index 9867a0dea..92a33cb92 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -1150,10 +1150,14 @@ def run_mxh_pseudo_three_step(self) -> bool: if not self.check_input_files(self.xcelium_ext): return False + digital_files = self.get_setting("sim.inputs.input_files") acf = self.get_setting("sim.xcelium.acf") amscf = self.get_setting("sim.xcelium.amscf") - connectlibs = self.get_setting("sim.xcelium.connectlibs") + + #Alt Connectlibs + connectlibs = self.get_setting("vlsi.technologies.extralib.library.connectlibs") + #connectlibs = self.get_setting("sim.xcelium.connectlibs") anamodels_dir = self.get_setting("sim.xcelium.schematics") connrules = self.get_setting("sim.xcelium.amsconnrules") From cd5432fd7152af5e56938c5dabeb17c5f8bc10f1 Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Wed, 22 May 2024 15:41:15 -0700 Subject: [PATCH 3/9] collapsed multiple namespaces into extra_libs, added user-formatted commands, and removed hardcoded options --- hammer/sim/xcelium/__init__.py | 395 +++++--------------------- hammer/sim/xcelium/defaults.yml | 18 +- hammer/sim/xcelium/defaults_types.yml | 17 +- 3 files changed, 72 insertions(+), 358 deletions(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index 92a33cb92..b5417c766 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -381,7 +381,7 @@ def generate_sim_tcl(self) -> bool: f.close() return True - def generate_amscf(self) -> bool: + """def generate_amscf(self) -> bool: # Open AMS control file template for read. # Hardcoded path for now t = open("amscf_template.scs", "r") @@ -434,110 +434,7 @@ def generate_amscf(self) -> bool: # Close files properly. t.close() f.close() - return True - - def retrieve_files(path, exts=[], output_type=str, relative=True): - """ - Returns a list or line-seperated string of all filepaths in a directory and any of its subdirectories. - """ - file_list = [] - extslower = [extension.lower() for extension in exts] - exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] - - for (root, directories, filenames) in os.walk(path): - for filename in filenames: - file_ext = (os.path.splitext(filename)[1]).lower() - rel_root = os.path.relpath(root) - if (relative): - filepath = os.path.join(rel_root, filename) - else: - filepath = f"{os.path.join(root, filename)}" - - if (not exts): - file_list.append(filepath) - elif (file_ext in exts_proc): - file_list.append(filepath) - - if (output_type is str): - return "".join(file_list) + "\n" - elif (output_type is list): - return file_list - else: - return "".join(file_list) + "\n" - - def generate_ams_opts(self): #Hardcoded to example - ams_opts_def = { - "ams": False, - "gen_amscf": False, - "amsconnrules": "" - } - - bool_list = ["ams", "gen_amscf"] - - ### Read in, process, and convert AMS opts into formatted string. - ams_opts = self.get_settings_from_dict(ams_opts_def, key_prefix=self.tool_config_prefix()) - ams_opts_proc = ams_opts.copy() - - # Process special AMS opts - if ams_opts_proc ["ams"]: - ams_opts_proc ["ams"] = "-ams_flex" - else: - ams_opts_proc ["ams"] = "" - - if ams_opts_proc ["gen_amscf"]: - pass - else: - ams_opts_proc ["gen_amscf"] = "" - - if ams_opts_proc ["amsconnrules"]: - ams_opts_proc ["amsconnrules"] = "ConnRules_multpower" - else: - ams_opts_proc ["amsconnrules"] = "" - - # Process non-specified standard AMS opts - for (opt, setting) in ams_opts_proc.items(): - if (opt not in bool_list and setting is not None): - ams_opts_proc [opt] = f"-{opt} {setting}" - - # Convert AMS opt dictionary into formatted string - ams_opt_str = "" - for (opt, setting) in ams_opts_proc.items(): - ams_opt_str += f"{setting}\n" - - ### Read in disciplines file, if it exists. - disciplines = self.get_setting("sim.xcelium.disciplines") - - if (disciplines): - df = open(disciplines, "r") - discpline_str = df.read() + "\n" - df.close() - else: - discpline_str = "" - - ### Read in A/MS schematics, control files, connect libs, and convert them into formatted strings - cwd = os.getcwd() - schematicdir = os.path.join(cwd, self.get_setting("sim.xcelium.schematics")) - connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) - #acf = os.path.join(cwd, self.get_setting("sim.xcelium.acf")) + "\n" - amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) + "\n" - - # Retrieve formatted string of schematic files - schematics = self.retrieve_files(schematicdir, exts=[".sp", ".vams"]) - - # Retrieve formatted string of connect files - connectlibs = self.retrieve_files(connectdir, exts=[".vams"]) - - ### Attach additional options - additional_opts = "-spectre_args \"++aps\"\n-messages \n-ieinfo \n" - - ### Create and return combined string - ams_opt_header = "# AMS OPTS:\n" - formatted_opts = ams_opt_header + ams_opt_str + discpline_str + additional_opts + "\n" - - ams_file_header = "# AMS FILES:\n" - formatted_files = ams_file_header + amscf + connectlibs + "\n" - - return formatted_files + formatted_opts + return True""" def attach_opts(self, filepath, attachment): f = open(filepath, "a+") @@ -558,159 +455,6 @@ def get_disciplines(self) -> str: else: return "" - - - if (disciplines): - dpath = os.path.join(cwd, disciplines) - df = open(dpath, "r") - discpline_str = df.read() - disciplines = "\n".split(discpline_str) - discipline_opts = [opt for opt in disciplines if opt != ""] - df.close() - else: - discipline_opts = [] - - if discipline_opts: - discipline_opts_proc = [f"-SETD \"{opt}\"" for opt in discipline_opts] - else: - discipline_opts = [] - - return discipline_opts_proc - - def extract_ams_opts(self, step): - ams_opts = self.get_setting(f"{self.tool_config_prefix()}.ams_opts", []) - cwd = os.getcwd() - match (step): - case ("compile"): - ### Compiles only the files (Digital, V-AMS) - - # Gather compile-time options - - compile_opts = self.extract_ams_compile_opts()[0] - compile_opts_list = []#[setting for setting in compile_opts.values()] - - # Gather AMS files from specified locations. - amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) - connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) - - # Gather connect libraries (currently manually added, in future pull from Xcelium install) & connect rules - connectlibs = self.retrieve_files(connectdir, exts=[".vams"], output_type=str) - connrules = os.path.join(cwd, self.get_setting(f"{self.tool_config_prefix()}.amsconnrules")) + ".vams" - - # Add AMS files to compile stage. - #compile_opts_list.append(connectlibs) - #compile_opts_list.append(connrules) - compile_opts_list.append(f"{amscf}") - - #ams_compile_inputs = connectlibs + connrules - #compile_opts_list.append(f"-AMSINPUT\n{ams_compile_inputs}\n") - - return compile_opts_list - - case ("elaborate"): - ### Parses/compiles the source files and elaborates the design. - - # Gather AMS Control File - amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) - - # Gather elaboration stage options - elab_opts_proc = self.extract_ams_elab_opts(ams_opts)[0] - elab_opts_str = "" - for (opt, setting) in elab_opts_proc.items(): - elab_opts_str += setting + "\n" - - # Gather and add discipline options, if any. - discpline_opts = self.get_disciplines() - elab_opts_str += discpline_opts - - # Gather AMS Files from specified locations. - amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) - connectdir = os.path.join(cwd, self.get_setting("sim.xcelium.connectlibs")) - - # Gather connect libraries and rules. - connectlibs = self.retrieve_files(connectdir, exts=[".vams"], output_type=str) - connrules = os.path.join(cwd, self.get_setting(f"{self.tool_config_prefix()}.amsconnrules")) + ".vams" - - # Add AMS files to elaboration stage as AMS inputs. - elab_opts_str += f"\n{connectlibs}\n" - elab_opts_str += f"{connrules}\n" - - # Add AMS Control File. - #elab_opts_str += f"{amscf}\n" - - return elab_opts_str - case ("sim"): - return self.extract_ams_sim_opts(ams_opts) - return - - def extract_ams_compile_opts(self) -> Tuple[Dict[str, str], Dict[str, str]]: - ams_compile_opts_def = { - "genamscf": False - } - - ams_compile_opts = self.get_settings_from_dict(ams_compile_opts_def, key_prefix=self.tool_config_prefix()) - ams_compile_opts_proc = ams_compile_opts.copy() - bool_list = ["genamscf"] - if ams_compile_opts_proc ["genamscf"]: - pass #In future, generate AMSCF from template and place it in specified location - else: - pass - - for opt, setting in ams_compile_opts_proc.items(): - if opt not in bool_list and setting is not None: - ams_compile_opts_proc [opt] = f"-{opt} {setting}" - - return ams_compile_opts_proc, ams_compile_opts - - def extract_ams_elab_opts(self, ams_opts) -> Tuple[Dict[str, str], Dict[str, str]]: - ams_elab_opts_def = { - "ams_flex": False, - "amsconnrules": None, - "ieinfo": False - } - - bool_list = ["ams_flex", "ieinfo"] - - ams_elab_opts = {} - for opt in ams_opts: - ams_elab_opts [opt] = opt - ams_elab_opts_proc = ams_elab_opts.copy() - - for opt in ams_elab_opts_proc: - if opt in bool_list: - ams_elab_opts_proc [opt] = f"-{opt}" - - # Specific settings for AMS connection rules - amsconnrules = self.get_setting(f"{self.tool_config_prefix()}.amsconnrules") - amsconnrules_basepath, amsconnrules_name = os.path.split(amsconnrules) - if amsconnrules: - ams_elab_opts_proc ["amsconnrules"] = f"-AMSCONNRULES\n{amsconnrules_name}" - elif ("amsconnrules" not in ams_elab_opts): - ams_elab_opts_proc.pop("amsconnrules") - - # Extra args - - ams_elab_opts_proc ["spectre_args"] = f"-SPECTRE_ARGS \"++aps\"" - #ams_elab_opts_proc ["AMSINPUT"] = f"-AMSINPUT {amscf}" - - amscf = self.get_setting(f"{self.tool_config_prefix()}.amscf") - ams_elab_opts_proc ["analogcontrol"] = f"-ANALOGCONTROL {amscf}" - #ams_elab_opts_proc ["AMS_ELAB"] = f"-AMS_ELAB" - - return ams_elab_opts_proc, ams_elab_opts - - def extract_ams_sim_opts(self, ams_opts): - cwd = os.getcwd() - amscf = os.path.join(cwd, self.get_setting("sim.xcelium.amscf")) - sim_opts = [f"-ANALOGCONTROL {amscf}", f"-AMS_FLEX", f"-SPECTRE_ARGS \"++aps\""] - sim_opts_str = "" - - sim_opts_str += f"-ANALOGCONTROL {amscf}" - sim_opts_str += f"-AMS_FLEX" - sim_opts_str += f"-SPECTRE_ARGS \"++aps\"" - - return sim_opts - def compile_xrun(self) -> bool: if not os.path.isfile(self.xcelium_bin): @@ -777,17 +521,12 @@ def elaborate_xrun(self) -> bool: ### If AMS enabled, submit options but do not run elaborate sub-step. if self.get_setting(f"{self.tool_config_prefix()}.ams"): return True - """if self.get_setting(f"{self.tool_config_prefix()}.ams"): - ams_elab_opts = self.extract_ams_opts("elaborate") - self.attach_opts(arg_file_path, ams_elab_opts) - #Add all necessary AMS specific commands, remove other commands as necessary""" self.run_executable(args, cwd=self.run_dir) return True def sim_xrun(self) -> bool: sim_opts = self.extract_sim_opts()[1] - ams_opts = self.extract_ams_opts("sim") sim_cmd_opts = self.get_setting(f"{self.sim_input_prefix}.options", []) sim_opts_removal = ["tb_name", "input_files", "incdir"] xrun_opts_removal = ["enhanced_recompile", "mce"] @@ -822,16 +561,15 @@ def sim_xrun(self) -> bool: return True - def sift_exts(list, exts): + def sift_exts(filelist, exts=[]): """ Returns a list of filepaths whose filenames contain any of the extensions in the specified extension list """ - exts_lower = [extension.lower() for extension in exts] - exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in exts_lower] - list_proc = [os.path.splitext(path.lower()) for path in list] - sifted = [f"{name}{ext}" for (name, ext) in list_proc if ext in exts_proc] + exts_lower = list(map(str.lower, exts)) + filelist_lower = list(map(str.lower, filelist)) + sifted = list(filter(lambda x: os.path.splitext(x)[1] in exts_lower, filelist_lower)) return sifted @@ -927,14 +665,19 @@ def analog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> return f"{control}" - def discipline_collector(discipline_filename) -> str: + def discipline_collector(self, discipline_filename) -> str: """ Returns a formatted string of all disciplines from the disciplines.txt file """ ### Read in disciplines file, if it exists. - disciplines = os.path.join(os.getcwd(), discipline_filename) - dpath = os.path.join(os.getcwd(), disciplines) - if disciplines: + + + dpath = os.path.join(os.getcwd(), discipline_filename) + + if not os.path.isfile(dpath): + self.logger.error(f"No discipline file found at {dpath}.") + + if dpath: df = open(dpath, "r") discipline_opts = df.read() disciplines_formatted = re.sub("\n", " \\\n", discipline_opts) + " \\" @@ -943,12 +686,13 @@ def discipline_collector(discipline_filename) -> str: else: return "" - def option_preparer(self, opts, pseudo_step=True) -> str: + def option_preparer(self, opts, addt_opts, pseudo_step=True) -> str: """ Returns a formatted string of all provided AMS options and their arguments """ bool_list = ["ams", "disciplines", "gen_amscf"] opts_proc = opts.copy() + header = "" if not opts: return "" @@ -965,7 +709,7 @@ def option_preparer(self, opts, pseudo_step=True) -> str: opts_proc ["gen_amscf"] = "" if opts ["disciplines"]: - opts_proc ["disciplines"] = xcelium.discipline_collector(opts["disciplines"]) + header += self.discipline_collector(opts["disciplines"]) + "\n" if opts ["amsconnrules"]: opts_proc ["amsconnrules"] = opts["amsconnrules"] @@ -976,12 +720,13 @@ def option_preparer(self, opts, pseudo_step=True) -> str: opts_proc = digital_opts #Fixed Extra Opts - opts_proc ["timescale"] = self.get_setting("sim.inputs.timescale") - opts_proc ["input"] = "probe.tcl" - opts_proc ["access"] = "+rwc" - opts_proc ["messages"] = "" - opts_proc ["spectre_args"] = "\"++aps\"" - opts_proc ["ieinfo"] = "" + #opts_proc ["timescale"] = self.get_setting("sim.inputs.timescale") + #opts_proc ["input"] = "probe.tcl" + #opts_proc ["access"] = "+rwc" + #opts_proc ["messages"] = "" + #opts_proc ["spectre_args"] = "\"++aps\"" + #opts_proc ["ieinfo"] = "" + opts_proc = {opt:setting for (opt, setting) in opts_proc.items() if opt not in bool_list and setting is not None} @@ -1002,12 +747,27 @@ def option_preparer(self, opts, pseudo_step=True) -> str: opts_rev = {k: v for k, v in opts_proc.items() if v} opts_proc_str = "\n".join(opts_rev.values()) - return f"{opts_proc_str}" + + # Attach user-defined commands, if any are included + if (addt_opts): + opts_proc_str += " \\\n" + + footer = " \\\n".join(addt_opts) + + return header + f"{opts_proc_str}" + footer def generate_amscf(self, template_filename, amscontrol_filename) -> bool: """ Creates an AMS control file based on templated format with available analog models & schematics """ + + # Get analog models, schematics from directories specified in extralibs + extralib = self.get_setting("vlsi.technologies.extra_libraries") + extralib_dict = extralib[0] + anamodels_dir = extralib_dict["anamodels"] + schematics_dir = extralib_dict["schematics"] + + # Open AMS control file template for read. template_path = os.path.join(os.getcwd(), template_filename) t = open(template_path, "r") @@ -1017,11 +777,11 @@ def generate_amscf(self, template_filename, amscontrol_filename) -> bool: f = open(amscontrol_path, "w+") # Get normalized, absolute paths for analog model files - model_path = os.path.join(os.getcwd(), self.get_setting("sim.xcelium.anamodels")) + model_path = os.path.join(os.getcwd(), anamodels_dir) models = [os.path.normpath(modelfile.path) for modelfile in os.scandir(model_path)] # Get normalized, absolute paths for analog schematic files - schematic_path = os.path.join(os.getcwd(), self.get_setting("sim.xcelium.schematics")) + schematic_path = os.path.join(os.getcwd(), schematics_dir) schematics = [os.path.normpath(schematic.path) for schematic in os.scandir(schematic_path)] # Warnings for missing files. @@ -1086,7 +846,7 @@ def option_extractor(self, argfile_names=[]): return opts - def scriptwriter(self, collect=False, sourcedir="", blacklist=[], sourcelist=[], options={"ams": False, "disciplines": "", "amsconnrules": "", "gen_amscf": False}): + def scriptwriter(self, options, additional_options, collect=False, sourcedir="", blacklist=[], sourcelist=[]): """ Writes all prepared files and arguments to the run_mxh shell script """ @@ -1105,42 +865,19 @@ def scriptwriter(self, collect=False, sourcedir="", blacklist=[], sourcelist=[], f.write(xcelium.analog_preparer(collect, sourcelist, sourcedir, blacklist)) # Write Options - f.write(self.option_preparer(options)) + f.write(self.option_preparer(options, additional_options)) f.close() return - def run_mxh(self) -> bool: - if not os.path.isfile(self.xcelium_bin): - self.logger.error(f"Xcelium (xrun) binary not found at {self.xcelium_bin}.") - return False - - if not self.check_input_files(self.xcelium_ext): - return False + def name_finder(self, name, sourcelist): + # Helper function, should probably be moved later + sourcelist_proc = [element.lower() for element in sourcelist if (type(element) == str)] + for element in sourcelist_proc: + if (name in element): + return element - digital_files = self.get_setting("sim.inputs.input_files") - acf = self.get_setting("sim.xcelium.acf") - amscf = self.get_setting("sim.xcelium.amscf") - connectlibs = self.get_setting("sim.xcelium.connectlibs") - anamodels_dir = self.get_setting("sim.xcelium.schematics") - - connrules = self.get_setting("sim.xcelium.amsconnrules") - ams_opts = self.get_setting("sim.xcelium.ams_opts") - - source = digital_files + [acf, amscf, connectlibs] - - ams_opts_dict = { - "ams": self.get_setting("sim.xcelium.ams"), - "disciplines": self.get_setting("sim.xcelium.disciplines"), - "amsconnrules": self.get_setting("sim.xcelium.amsconnrules"), - "gen_amscf": self.get_setting("sim.xcelium.genamscf") - } - - self.scriptwriter(collect=True, sourcedir="src/", blacklist=["src/amscf_template.scs", "src/ams_control/acf.scs"], options=ams_opts_dict) - - self.update_submit_options() - self.run_executable(["./run_mxh"], cwd=self.run_dir) - return True + return "" def run_mxh_pseudo_three_step(self) -> bool: if not os.path.isfile(self.xcelium_bin): @@ -1150,35 +887,37 @@ def run_mxh_pseudo_three_step(self) -> bool: if not self.check_input_files(self.xcelium_ext): return False - digital_files = self.get_setting("sim.inputs.input_files") acf = self.get_setting("sim.xcelium.acf") amscf = self.get_setting("sim.xcelium.amscf") - #Alt Connectlibs - connectlibs = self.get_setting("vlsi.technologies.extralib.library.connectlibs") - #connectlibs = self.get_setting("sim.xcelium.connectlibs") - anamodels_dir = self.get_setting("sim.xcelium.schematics") + # Extralibs Autorecognition + extralib = self.get_setting("vlsi.technologies.extra_libraries") + extralib_dict = extralib[0] - connrules = self.get_setting("sim.xcelium.amsconnrules") - ams_opts = self.get_setting("sim.xcelium.ams_opts") + #ams_opts = self.get_setting("sim.xcelium.ams_opts") - source = digital_files + [acf, amscf, connectlibs] + #source = digital_files + [acf, amscf, connectlibs] ams_opts_dict = { "ams": self.get_setting("sim.xcelium.ams"), - "disciplines": self.get_setting("sim.xcelium.disciplines"), - "amsconnrules": self.get_setting("sim.xcelium.amsconnrules"), - "gen_amscf": self.get_setting("sim.xcelium.genamscf") + "disciplines": extralib_dict["disciplines"], + #"disciplines": self.name_finder("disciplines", extralibs), + "amsconnrules": extralib_dict["amsconnrules"], + "gen_amscf": extralib_dict["gen_amscf"] } - self.scriptwriter(collect=True, sourcedir="src/", blacklist=["src/amscf_template.scs", "src/ams_control/acf.scs"], options=ams_opts_dict) + ams_addt_opts = extralib_dict["ams_addt_opts"] + + filepath_blacklist = extralib_dict["filepath_blacklist"] + [extralib_dict["amscf_template"]] + + self.scriptwriter(options=ams_opts_dict, additional_options=ams_addt_opts, collect=True, sourcedir="src/", blacklist=filepath_blacklist) # Extract digital-only options from compile, elab, and sim argfiles combined_opts = self.option_extractor(["xrun_compile.arg", "xrun_elab.arg", "xrun_sim.arg"]) self.update_submit_options() - self.run_executable(["./run_mxh"], cwd=self.run_dir) + self.run_executable([self.xcelium_bin, './run_mxh'], cwd=self.run_dir) return True tool = xcelium diff --git a/hammer/sim/xcelium/defaults.yml b/hammer/sim/xcelium/defaults.yml index 9fbbf4daa..8943244e4 100644 --- a/hammer/sim/xcelium/defaults.yml +++ b/hammer/sim/xcelium/defaults.yml @@ -47,19 +47,9 @@ sim.xcelium: acf: null # Specifies location of ams control file. amscf: null - # If true, create a partially populated template of the AMS control file. - gen_amscf: False - # To be removed in future. - anamodels: null - # To be removed in future. - schematics: null # List of strings specifying ams options. ams_opts: null - # Path to .txt file containing formatted disciplines. - disciplines: null - # Path to connect library files (cross-power domain libs). - connectlibs: null - # Path to file specifying cross-power domain connection rules (if applicable). - amsconnrules: null - # Path to ams control file template. - amscf_template: null + compile_opts: null + elab_opts: null + + diff --git a/hammer/sim/xcelium/defaults_types.yml b/hammer/sim/xcelium/defaults_types.yml index 9158d1372..3b65c14f0 100644 --- a/hammer/sim/xcelium/defaults_types.yml +++ b/hammer/sim/xcelium/defaults_types.yml @@ -30,22 +30,7 @@ sim.xcelium: # Path to ams control file. amscf: Optional[str] # If true, create a partially populated template of the AMS control file. - gen_amscf: Optional[bool] - # Path to analog models directory. - # To be removed in future, pulled instead from PDK library models - anamodels: Optional[str] - # Path to analog schematics directory. - # To be removed in future, pulled from EXTRA_LIBS - schematics: Optional[str] - # Path to .txt file containing formatted disciplines. - disciplines: Optional[str] - # Path to connect library files (cross-power domain libs). - connectlibs: Optional[str] - # Path to file specifying cross-power domain connection rules (if applicable). - amsconnrules: Optional[str] - # Path to ams control file template. - amscf_template: Optional[str] compile_opts: Optional[list[str]] elab_opts: Optional[list[str]] - ams_opts: Optional[list[str]] \ No newline at end of file + ams_opts: Optional[list[str]] From 628327b820c300dd9fdd0ad76b3a9136016e0123 Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Fri, 31 May 2024 23:22:17 -0700 Subject: [PATCH 4/9] shifted some functions to hammer utils --- hammer/sim/xcelium/__init__.py | 83 ++++++++-------------------------- hammer/utils/__init__.py | 44 ++++++++++++++++++ 2 files changed, 64 insertions(+), 63 deletions(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index b5417c766..f0400b8e7 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -24,6 +24,7 @@ from hammer.vlsi import HammerSimTool, HammerToolStep, HammerLSFSubmitCommand, HammerLSFSettings from hammer.logging import HammerVLSILogging from hammer.common.cadence import CadenceTool +from hammer.utils import retrieve_files, sift_exts # MXHammer version @@ -318,6 +319,10 @@ def generate_saif_tcl_cmd(self) -> str: saif_args = "" # Process saif options + saif_start_time: Optional[str] = None + saif_end_time: Optional[str] = None + saif_start_trigger_raw: Optional[str] = None + saif_end_trigger_raw: Optional[str] = None if saif_opts["mode"] == "time": saif_start_time = saif_opts["start_time"] saif_end_time = saif_opts["end_time"] @@ -334,12 +339,16 @@ def generate_saif_tcl_cmd(self) -> str: if saif_opts["mode"] is not None: if saif_opts["mode"] == "time": + assert saif_start_time + assert saif_end_time stime = TimeValue(saif_start_time) etime = TimeValue(saif_end_time) saif_args = saif_args + f'dumpsaif -output ucli.saif -overwrite -scope {prefix} -start {stime.value_in_units("ns")}ns -stop{etime.value_in_units("ns")}ns' elif saif_opts["mode"] == "full": saif_args = saif_args + f"dumpsaif -output ucli.saif -overwrite -scope {prefix}" elif saif_opts["mode"] == "trigger_raw": + assert saif_start_trigger_raw + assert saif_end_trigger_raw saif_args = saif_args + f"dumpsaif -output ucli.saif -overwrite -scope {prefix} {saif_start_trigger_raw} {saif_end_trigger_raw}" return saif_args @@ -530,19 +539,13 @@ def sim_xrun(self) -> bool: sim_cmd_opts = self.get_setting(f"{self.sim_input_prefix}.options", []) sim_opts_removal = ["tb_name", "input_files", "incdir"] xrun_opts_removal = ["enhanced_recompile", "mce"] - ams_opts_removal = [] - - #if (self.get_setting(f"sim.xcelium.ams")): sim_cmd_opts.extend(ams_opts) - sim_cmd_opts = ('SIMULATION', sim_cmd_opts) if not sim_opts["execute_sim"]: self.logger.warning("Not running any simulations because sim.inputs.execute_sim is unset.") return True - - - arg_file_path = self.generate_arg_file("xrun_sim.arg", "HAMMER-GEN XRUN SIM ARG FILE", [sim_cmd_opts, 'SIMULATION'], + arg_file_path = self.generate_arg_file("xrun_sim.arg", "HAMMER-GEN XRUN SIM ARG FILE", [sim_cmd_opts], sim_opt_removal = sim_opts_removal, xrun_opt_removal = xrun_opts_removal) args =[self.xcelium_bin] @@ -550,68 +553,22 @@ def sim_xrun(self) -> bool: self.generate_sim_tcl() self.update_submit_options() - - ### If AMS enabled, submit options and procede to AMS single-step function - if self.get_setting(f"{self.tool_config_prefix()}.ams"): - self.run_mxh_pseudo_three_step() - return True - - self.run_executable(args, cwd=self.run_dir) return True - def sift_exts(filelist, exts=[]): - """ - Returns a list of filepaths whose filenames contain any of the extensions in the specified extension list - """ - - - exts_lower = list(map(str.lower, exts)) - filelist_lower = list(map(str.lower, filelist)) - sifted = list(filter(lambda x: os.path.splitext(x)[1] in exts_lower, filelist_lower)) - - return sifted - - def retrieve_files(path, exts=[], output_type=str, relative=True): - """ - Returns a list or line-seperated string of all filepaths in a directory and any of its subdirectories. - """ - file_list = [] - extslower = [extension.lower() for extension in exts] - exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] - - for (root, directories, filenames) in os.walk(path): - for filename in filenames: - file_ext = (os.path.splitext(filename)[1]).lower() - rel_root = os.path.relpath(root) - if (relative): - filepath = os.path.join(rel_root, filename) - else: - filepath = f"{os.path.join(root, filename)}" - - if (not exts): - file_list.append(filepath) - elif (file_ext in exts_proc): - file_list.append(filepath) - - if (output_type is str): - return "".join(file_list) + "\n" - elif (output_type is list): - return file_list - else: - return "".join(file_list) + "\n" + - def vlog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + def vlog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: """ Returns a formatted string of all verilog/VHDL files in the source """ vlog = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vlog_list = xcelium.retrieve_files(sourcepath, [".v", ".vhdl"], list) + vlog_list = retrieve_files(sourcepath, [".v", ".vhdl"], list) else: - vlog_list = xcelium.sift_exts(sourcelist, [".v"]) + vlog_list = sift_exts(sourcelist, [".v"]) if (blacklist and vlog_list): for pathname in blacklist: @@ -623,16 +580,16 @@ def vlog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> s return f"{vlog}" - def vams_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + def vams_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: """ Returns a formatted string of all V-AMS files in the source """ vams = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vams_list = xcelium.retrieve_files(sourcepath, [".vams"], list) + vams_list = retrieve_files(sourcepath, [".vams"], list) else: - vams_list = xcelium.sift_exts(sourcelist, [".vams"]) + vams_list = sift_exts(sourcelist, [".vams"]) if (blacklist and vams_list): for pathname in blacklist: @@ -644,16 +601,16 @@ def vams_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> s return f"{vams}" - def analog_preparer(collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: + def analog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[]) -> str: """ Returns a formatted string of all analog (.scs) files in the source """ control = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - control_list = xcelium.retrieve_files(sourcepath, [".scs"], list) + control_list = retrieve_files(sourcepath, [".scs"], list) else: - control_list = xcelium.sift_exts(sourcelist, [".scs"]) + control_list = sift_exts(sourcelist, [".scs"]) if (blacklist and control_list): for pathname in blacklist: diff --git a/hammer/utils/__init__.py b/hammer/utils/__init__.py index aea332e88..b3068579b 100644 --- a/hammer/utils/__init__.py +++ b/hammer/utils/__init__.py @@ -368,6 +368,50 @@ def compare_types(a: Any, b: Any) -> bool: return None +def retrieve_files(path: str, exts=[], output_type="str", relative=True): + """ + Returns a list or line-seperated string of all filepaths in a directory and any of its subdirectories + with the specified extensions, returning all filepaths within that directory if no extension is specified. + + :param path: Specifies the filepath to the directory + :param exts: List of strings specifying whitelisted extensions + :param output_type: Specifies whether output will be a list or string, by "list" or "str" + :param relative: If true, return filepaths relative to the directory. Else, return absolute filepaths. + """ + file_list = [] + extslower = [extension.lower() for extension in exts] + exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] + + for (root, directories, filenames) in os.walk(path): + for filename in filenames: + file_ext = (os.path.splitext(filename)[1]).lower() + rel_root = os.path.relpath(root) + if (relative): + filepath = os.path.join(rel_root, filename) + else: + filepath = f"{os.path.join(root, filename)}" + + if (not exts): + file_list.append(filepath) + elif (file_ext in exts_proc): + file_list.append(filepath) + + if (output_type is "str"): + return "".join(file_list) + "\n" + elif (output_type is "list"): + return file_list + else: + return "".join(file_list) + "\n" + +def sift_exts(filelist, exts=[]): + """ + Returns a list of filepaths whose filenames contain any of the extensions in the specified extension list + """ + exts_lower = list(map(str.lower, exts)) + filelist_lower = list(map(str.lower, filelist)) + sifted = list(filter(lambda x: os.path.splitext(x)[1] in exts_lower, filelist_lower)) + + return sifted # Contributors: Be sure to add to this list if you need to call get_filetype @unique From c1f4fdf831a275b488c1f412d003931b4a21535f Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Fri, 31 May 2024 23:36:28 -0700 Subject: [PATCH 5/9] extra fixes --- hammer/sim/xcelium/__init__.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index f0400b8e7..e1cb1839c 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -675,15 +675,6 @@ def option_preparer(self, opts, addt_opts, pseudo_step=True) -> str: digital_opts = self.option_extractor(["xrun_compile.arg", "xrun_elab.arg", "xrun_sim.arg"]) digital_opts.update(opts_proc) # Should any keys match, AMS arguments take precedence opts_proc = digital_opts - - #Fixed Extra Opts - #opts_proc ["timescale"] = self.get_setting("sim.inputs.timescale") - #opts_proc ["input"] = "probe.tcl" - #opts_proc ["access"] = "+rwc" - #opts_proc ["messages"] = "" - #opts_proc ["spectre_args"] = "\"++aps\"" - #opts_proc ["ieinfo"] = "" - opts_proc = {opt:setting for (opt, setting) in opts_proc.items() if opt not in bool_list and setting is not None} @@ -815,11 +806,11 @@ def scriptwriter(self, options, additional_options, collect=False, sourcedir="", f.write("#!/bin/csh -f\n#\nxrun -clean \\\n") # Write Digital Files - f.write(xcelium.vlog_preparer(collect, sourcelist, sourcedir, blacklist)) - f.write(xcelium.vams_preparer(collect, sourcelist, sourcedir, blacklist)) + f.write(self.vlog_preparer(collect, sourcelist, sourcedir, blacklist)) + f.write(self.vams_preparer(collect, sourcelist, sourcedir, blacklist)) # Write Analog Files - f.write(xcelium.analog_preparer(collect, sourcelist, sourcedir, blacklist)) + f.write(self.analog_preparer(collect, sourcelist, sourcedir, blacklist)) # Write Options f.write(self.option_preparer(options, additional_options)) From abeb8866fc8619bcb876ed5791ccdea302681d2f Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Fri, 31 May 2024 23:55:07 -0700 Subject: [PATCH 6/9] added type fix for retrieve_files --- hammer/sim/xcelium/__init__.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index e1cb1839c..0bc2eb042 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -566,7 +566,7 @@ def vlog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[] vlog = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vlog_list = retrieve_files(sourcepath, [".v", ".vhdl"], list) + vlog_list = retrieve_files(sourcepath, [".v", ".vhdl"], "list") else: vlog_list = sift_exts(sourcelist, [".v"]) @@ -587,7 +587,7 @@ def vams_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[] vams = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vams_list = retrieve_files(sourcepath, [".vams"], list) + vams_list = retrieve_files(sourcepath, [".vams"], "list") else: vams_list = sift_exts(sourcelist, [".vams"]) @@ -608,7 +608,7 @@ def analog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist= control = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - control_list = retrieve_files(sourcepath, [".scs"], list) + control_list = retrieve_files(sourcepath, [".scs"], "list") else: control_list = sift_exts(sourcelist, [".scs"]) From 28659e22baaee59829d724c82bd97dae068e347a Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Sat, 1 Jun 2024 00:00:15 -0700 Subject: [PATCH 7/9] added list-specific retrieve_files --- hammer/sim/xcelium/__init__.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index 0bc2eb042..f56197940 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -556,6 +556,8 @@ def sim_xrun(self) -> bool: self.run_executable(args, cwd=self.run_dir) return True + def retrieve_file_list(self, path, exts=[], relative=True) -> list: + return retrieve_files(path, exts, output_type="list") @@ -566,7 +568,7 @@ def vlog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[] vlog = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vlog_list = retrieve_files(sourcepath, [".v", ".vhdl"], "list") + vlog_list = self.retrieve_file_list(sourcepath, [".v", ".vhdl"]) else: vlog_list = sift_exts(sourcelist, [".v"]) @@ -587,7 +589,7 @@ def vams_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist=[] vams = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - vams_list = retrieve_files(sourcepath, [".vams"], "list") + vams_list = self.retrieve_file_list(sourcepath, [".vams"]) else: vams_list = sift_exts(sourcelist, [".vams"]) @@ -608,7 +610,7 @@ def analog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist= control = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - control_list = retrieve_files(sourcepath, [".scs"], "list") + control_list = self.retrieve_files(sourcepath, [".scs"]) else: control_list = sift_exts(sourcelist, [".scs"]) From e12ff84631aee670c3ca1bd46ac630271348517a Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Sat, 1 Jun 2024 00:02:58 -0700 Subject: [PATCH 8/9] small fix --- hammer/sim/xcelium/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index f56197940..4b481527f 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -610,7 +610,7 @@ def analog_preparer(self, collect=False, sourcelist=[], sourcedir="", blacklist= control = "" if (collect): sourcepath = os.path.join(os.getcwd(), sourcedir) - control_list = self.retrieve_files(sourcepath, [".scs"]) + control_list = self.retrieve_file_list(sourcepath, [".scs"]) else: control_list = sift_exts(sourcelist, [".scs"]) From bd3ce8b2a6cfb0131243246595a63dc525b5a78b Mon Sep 17 00:00:00 2001 From: Miguel Gonzalez Date: Sat, 1 Jun 2024 00:12:29 -0700 Subject: [PATCH 9/9] fixed string issue --- hammer/sim/xcelium/__init__.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/hammer/sim/xcelium/__init__.py b/hammer/sim/xcelium/__init__.py index 4b481527f..afe9751a3 100644 --- a/hammer/sim/xcelium/__init__.py +++ b/hammer/sim/xcelium/__init__.py @@ -557,7 +557,26 @@ def sim_xrun(self) -> bool: return True def retrieve_file_list(self, path, exts=[], relative=True) -> list: - return retrieve_files(path, exts, output_type="list") + file_list = [] + extslower = [extension.lower() for extension in exts] + exts_proc = [f".{ext}" if ("." not in ext) else ext for ext in extslower] + + for (root, directories, filenames) in os.walk(path): + for filename in filenames: + file_ext = (os.path.splitext(filename)[1]).lower() + rel_root = os.path.relpath(root) + if (relative): + filepath = os.path.join(rel_root, filename) + else: + filepath = f"{os.path.join(root, filename)}" + + if (not exts): + file_list.append(filepath) + elif (file_ext in exts_proc): + file_list.append(filepath) + + return file_list +