|
| 1 | +# hammer-vlsi plugin for Questa |
| 2 | + |
| 3 | +from hammer.vlsi import HammerSimTool, DummyHammerTool, HammerToolStep, deepdict |
| 4 | +from hammer.config import HammerJSONEncoder |
| 5 | + |
| 6 | +import hammer.tech as hammer_tech |
| 7 | +from hammer.tech import HammerTechnologyUtils |
| 8 | + |
| 9 | +from typing import Dict, List, Any, Optional |
| 10 | +from decimal import Decimal |
| 11 | + |
| 12 | +import os |
| 13 | +import json |
| 14 | + |
| 15 | +class Questa(HammerSimTool, DummyHammerTool): |
| 16 | + |
| 17 | + # Simulation steps |
| 18 | + @property |
| 19 | + def steps(self) -> List[HammerToolStep]: |
| 20 | + return self.make_steps_from_methods([ |
| 21 | + self.qhsim |
| 22 | + ]) |
| 23 | + |
| 24 | + # Main simulation method |
| 25 | + def qhsim(self) -> bool: |
| 26 | + # Get Hammer settings |
| 27 | + tb_name = self.get_setting("sim.inputs.tb_name") # testbench main module |
| 28 | + tb_dut = self.get_setting("sim.inputs.tb_dut") # DUT instance name in testbench |
| 29 | + defines = self.get_setting("sim.inputs.defines") # macro definitions |
| 30 | + timescale = self.get_setting("sim.inputs.timescale") # timescale setup |
| 31 | + input_files_list = self.get_setting("sim.inputs.input_files") # input HDL files |
| 32 | + top_module = self.get_setting("sim.inputs.top_module") # DUT module name |
| 33 | + timing_annotated = self.get_setting("sim.inputs.timing_annotated") # using SDF |
| 34 | + wave_file = self.get_setting("sim.inputs.wave_file") # waveform format setup file |
| 35 | + vlog_args = self.get_setting("sim.inputs.vlog_args") # custom vlog arguments |
| 36 | + vopt_args = self.get_setting("sim.inputs.vopt_args") # custom vopt arguments |
| 37 | + vsim_args = self.get_setting("sim.inputs.vsim_args") # custom vsim arguments |
| 38 | + no_gui = self.get_setting("sim.inputs.no_gui") # do not open GUI |
| 39 | + questa_bin = self.get_setting("sim.questa.questa_bin") # Questa binary file |
| 40 | + # Create a Questa command file |
| 41 | + do_file = f"{self.run_dir}/{tb_name}.do" |
| 42 | + f = open(do_file, "w+") |
| 43 | + # Create the working library |
| 44 | + lib_name = f"work_{tb_name}" |
| 45 | + lib_dir = f"{self.run_dir}/{lib_name}" |
| 46 | + f.write("# Create the working library\n") |
| 47 | + f.write(f"rm -rf {lib_dir}\n") |
| 48 | + f.write(f"vlib {lib_dir}\n") |
| 49 | + f.write(f"vmap {lib_name} {lib_dir}\n") # potentially redundant |
| 50 | + # Compile the design units |
| 51 | + defines_list = [f"+define+{x}" for x in defines] |
| 52 | + defines_string = " ".join(defines_list) |
| 53 | + for i in range(len(input_files_list)): # converting relative paths to absolute |
| 54 | + if (input_files_list[i][0] != '/'): |
| 55 | + input_files_list[i] = os.getcwd() + '/' + input_files_list[i] |
| 56 | + input_files_string = " ".join(input_files_list) |
| 57 | + if self.level.is_gatelevel(): # add Verilog models of standard cells |
| 58 | + verilog_models_list = self.get_verilog_models() |
| 59 | + verilog_models_string = " ".join(verilog_models_list) |
| 60 | + input_files_string += ' ' + verilog_models_string |
| 61 | + f.write("# Compile the design units\n") |
| 62 | + f.write(f"vlog -work {lib_name} {defines_string} -timescale {timescale} \ |
| 63 | + {input_files_string} {vlog_args}\n") |
| 64 | + # Optimize the design |
| 65 | + sdf_args = "-nosdf +notimingchecks" |
| 66 | + if timing_annotated: |
| 67 | + sdf_corner = self.get_setting("sim.inputs.sdf_corner") |
| 68 | + # Convert relative paths to absolute (if needed) |
| 69 | + if self.sdf_file: # if SDF file is specified in input .yml |
| 70 | + self.sdf_file = self.sdf_file if self.sdf_file[0] == '/' else os.getcwd() + '/' + self.sdf_file |
| 71 | + # Generate SDF-related arguments |
| 72 | + sdf_args = f" +sdf_verbose -sdf{sdf_corner} /{tb_name}/{tb_dut}={self.sdf_file}" |
| 73 | + f.write("# Optimize the design\n") |
| 74 | + f.write(f"vopt -debugdb -work {lib_name} -timescale {timescale} \ |
| 75 | + {sdf_args} +acc {tb_name} -o opt_{tb_name} {vopt_args}\n") |
| 76 | + # Load the design |
| 77 | + f.write("# Load the design\n") |
| 78 | + f.write(f"vsim -debugDB -work {lib_name} opt_{tb_name} {vsim_args}\n") |
| 79 | + # Add waves |
| 80 | + f.write("# Add waves\n") |
| 81 | + if wave_file: # if waveform setup file is specified in input .yml |
| 82 | + wave_file = wave_file if wave_file[0] == '/' else "../../" + wave_file |
| 83 | + f.write(f"do {wave_file}\n") |
| 84 | + else: |
| 85 | + f.write(f"add wave -group TB -internal {tb_name}/*\n") # a group of all TB signals |
| 86 | + f.write(f"add wave -ports {tb_dut}/*\n") # DUT ports displayed individually |
| 87 | + f.write(f"add wave -group INT -r -internal {tb_dut}/*\n") # a group of internal DUT signal |
| 88 | + # Log simulation data |
| 89 | + f.write("# Log simulation data\n") |
| 90 | + f.write("log -r *\n") # potentially redundant |
| 91 | + # Run simulation (if enabled) |
| 92 | + if self.get_setting("sim.inputs.execute_sim"): |
| 93 | + f.write("# Run simulation\n") |
| 94 | + f.write("run -all\n") |
| 95 | + else: |
| 96 | + self.logger.warning("Not running any simulations because sim.inputs.execute_sim is unset.") |
| 97 | + # Close the Questa command file |
| 98 | + f.close() |
| 99 | + # Run Questa simulation |
| 100 | + args = [questa_bin] |
| 101 | + if no_gui: |
| 102 | + args.append("-c") # do not open GUI |
| 103 | + args.append("-do") |
| 104 | + args.append(f"{do_file}") |
| 105 | + self.run_executable(args, cwd=self.run_dir) |
| 106 | + return True |
| 107 | + |
| 108 | + # Fill output json file |
| 109 | + def fill_outputs(self) -> bool: |
| 110 | + self.output_waveforms = [] |
| 111 | + self.output_saifs = [] |
| 112 | + self.output_top_module = self.top_module |
| 113 | + self.output_tb_name = self.get_setting("sim.inputs.tb_name") |
| 114 | + self.output_tb_dut = self.get_setting("sim.inputs.tb_dut") |
| 115 | + self.output_level = self.get_setting("sim.inputs.level") |
| 116 | + return True |
| 117 | + |
| 118 | + # Get verilog models of standard cells |
| 119 | + def get_verilog_models(self) -> List[str]: |
| 120 | + verilog_sim_files = self.technology.read_libs([hammer_tech.filters.verilog_sim_filter], |
| 121 | + hammer_tech.HammerTechnologyUtils.to_plain_item) |
| 122 | + return verilog_sim_files |
| 123 | + |
| 124 | + def tool_config_prefix(self) -> str: |
| 125 | + return "sim.questa" |
| 126 | + |
| 127 | +tool = Questa |
0 commit comments