| """ |
| Copyright 2019 Google LLC |
| |
| Licensed under the Apache License, Version 2.0 (the "License"); |
| you may not use this file except in compliance with the License. |
| You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, software |
| distributed under the License is distributed on an "AS IS" BASIS, |
| WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| See the License for the specific language governing permissions and |
| limitations under the License. |
| |
| Regression script for RISC-V random instruction generator |
| """ |
| |
| import argparse |
| import os |
| import random |
| import re |
| import subprocess |
| import sys |
| |
| sys.path.insert(0, "../../vendor/google_riscv-dv/scripts") |
| sys.path.insert(0, "./riscv_dv_extension") |
| |
| from lib import * |
| from ibex_log_to_trace_csv import * |
| from spike_log_to_trace_csv import * |
| from ovpsim_log_to_trace_csv import * |
| from instr_trace_compare import * |
| |
| def process_cmd(keyword, cmd, opts, enable): |
| """ Process the compile and simulation command |
| |
| Args: |
| keyword : Keyword to search |
| cmd : Command to be processed |
| opts : Options to replace the keyword |
| enable : Option enable/disable |
| |
| Returns: |
| Processed command |
| """ |
| if enable == "1": |
| return re.sub(keyword, opts, cmd) |
| else: |
| return re.sub(keyword, "", cmd) |
| |
| |
| def get_simulator_cmd(simulator, simulator_yaml, en_cov, en_wave): |
| """ Setup the compile and simulation command for the generator |
| |
| Args: |
| simulator : RTL simulator used to run instruction generator |
| simulator_yaml : RTL simulator configuration file in YAML format |
| en_cov : Enable coverage dump |
| en_wave : Enable waveform |
| |
| Returns: |
| compile_cmd : RTL simulator command to compile the instruction generator |
| sim_cmd : RTL simulator command to run the instruction generator |
| """ |
| print("Processing simulator setup file : %s" % simulator_yaml) |
| yaml_data = read_yaml(simulator_yaml) |
| # Search for matched simulator |
| for entry in yaml_data: |
| if entry['tool'] == simulator: |
| print("Found matching simulator: %s" % entry['tool']) |
| compile_cmd = entry['compile']['cmd'] |
| for i in range(len(compile_cmd)): |
| compile_cmd[i] = process_cmd("<cov_opts>", compile_cmd[i], |
| entry['compile']['cov_opts'], en_cov) |
| compile_cmd[i] = process_cmd("<wave_opts>", compile_cmd[i], |
| entry['compile']['wave_opts'], en_wave) |
| sim_cmd = entry['sim']['cmd'] |
| sim_cmd = process_cmd("<cov_opts>", sim_cmd, entry['sim']['cov_opts'], en_cov) |
| sim_cmd = process_cmd("<wave_opts>", sim_cmd, entry['sim']['wave_opts'], en_wave) |
| return compile_cmd, sim_cmd |
| print ("Cannot find RTL simulator %0s" % simulator) |
| sys.exit(1) |
| |
| |
| def run_cmd(cmd): |
| """Run a command and return output |
| |
| Args: |
| cmd : shell command to run |
| |
| Returns: |
| command output |
| """ |
| try: |
| ps = subprocess.Popen(cmd, |
| shell=True, |
| universal_newlines=True, |
| stdout=subprocess.PIPE, |
| stderr=subprocess.STDOUT, |
| executable='/bin/bash') |
| except subprocess.CalledProcessError as exc: |
| print(ps.communicate()[0]) |
| sys.exit(1) |
| return ps.communicate()[0] |
| |
| |
| def get_seed(seed): |
| """Get the seed to run the generator |
| |
| Args: |
| seed : input seed |
| |
| Returns: |
| seed to run instruction generator |
| """ |
| if seed >= 0: |
| return seed |
| else: |
| return random.getrandbits(32) |
| |
| |
| def rtl_compile(compile_cmd, test_list, output_dir, lsf_cmd, opts, verbose): |
| """Run the instruction generator |
| |
| Args: |
| compile_cmd : Compile command |
| test_list : List of assembly programs to be compiled |
| output_dir : Output directory of the ELF files |
| lsf_cmd : LSF command to run compilation |
| opts : Compile options for the generator |
| verbose : Verbose logging |
| """ |
| # Compile the TB |
| print ("Compiling TB") |
| for cmd in compile_cmd: |
| cmd = re.sub("<out>", output_dir, cmd) |
| cmd = re.sub("<cmp_opts>", opts, cmd) |
| if verbose: |
| print("Compile command: %s" % cmd) |
| output = run_cmd(cmd) |
| if verbose: |
| print(output) |
| |
| |
| def rtl_sim(sim_cmd, test_list, output_dir, bin_dir, lsf_cmd, seed, opts, verbose): |
| """Run the instruction generator |
| |
| Args: |
| sim_cmd : Simulation command |
| test_list : List of assembly programs |
| output_dir : Simulation output directory |
| bin_dir : Directory of the ELF files |
| lsf_cmd : LSF command to run simulation |
| seed : Seed of RTL simulation |
| opts : Simulation options |
| verbose : Verbose logging |
| """ |
| # Run the RTL simulation |
| sim_cmd = re.sub("<out>", output_dir, sim_cmd) |
| sim_cmd = re.sub("<sim_opts>", opts, sim_cmd) |
| sim_cmd = re.sub("<cwd>", cwd, sim_cmd) |
| print ("Running RTL simulation...") |
| cmd_list = [] |
| for test in test_list: |
| for i in range(test['iterations']): |
| rand_seed = get_seed(seed) |
| sim_dir = output_dir + ("/%s.%d" %(test['test'], i)) |
| run_cmd(("mkdir -p %s" % sim_dir)) |
| os.chdir(sim_dir) |
| if verbose: |
| print("Run dir: %s" % sim_dir) |
| binary = ("%s/%s.%d.bin" % (bin_dir, test['test'], i)) |
| cmd = lsf_cmd + " " + sim_cmd.rstrip() + \ |
| (" +UVM_TESTNAME=%s " % test['rtl_test']) + \ |
| (" +bin=%s " % binary) + \ |
| (" +ntb_random_seed=%d " % rand_seed) + \ |
| (" -l sim.log ") |
| print("Running %s with %s" % (test['rtl_test'], binary)) |
| if verbose: |
| print(cmd) |
| try: |
| output = subprocess.check_output(cmd.split(), |
| timeout=1000, |
| universal_newlines=True) |
| except subprocess.CalledProcessError as exc: |
| print(output) |
| sys.exit(1) |
| if verbose: |
| print(output) |
| |
| |
| def compare(test_list, iss, output_dir, verbose): |
| """Compare RTL & ISS simulation reult |
| |
| Args: |
| test_list : List of assembly programs to be compiled |
| iss : List of instruction set simulators |
| output_dir : Output directory of the ELF files |
| verbose : Verbose logging |
| """ |
| report = ("%s/regr.log" % output_dir).rstrip() |
| for test in test_list: |
| for i in range(0, test['iterations']): |
| elf = ("%s/asm_tests/%s.%d.o" % (output_dir, test['test'], i)) |
| print("Comparing %s/DUT sim result : %s" % (iss, elf)) |
| run_cmd(("echo 'Test binary: %s' >> %s" % (elf, report))) |
| rtl_log = ("%s/rtl_sim/%s.%d/trace_core_00_0.log" % (output_dir, test['test'], i)) |
| rtl_csv = ("%s/rtl_sim/%s.%d/trace_core_00_0.csv" % (output_dir, test['test'], i)) |
| process_ibex_sim_log(rtl_log, rtl_csv) |
| iss_log = ("%s/instr_gen/%s_sim/%s.%d.log" % (output_dir, iss, test['test'], i)) |
| iss_csv = ("%s/instr_gen/%s_sim/%s.%d.csv" % (output_dir, iss, test['test'], i)) |
| if iss == "spike": |
| process_spike_sim_log(iss_log, iss_csv) |
| elif iss == "ovpsim": |
| process_ovpsim_sim_log(iss_log, iss_csv) |
| else: |
| print("Unsupported ISS" % iss) |
| sys.exit(1) |
| compare_trace_csv(rtl_csv, iss_csv, "ibex", iss, report) |
| passed_cnt = run_cmd("grep PASSED %s | wc -l" % report).strip() |
| failed_cnt = run_cmd("grep FAILED %s | wc -l" % report).strip() |
| summary = ("%s PASSED, %s FAILED" % (passed_cnt, failed_cnt)) |
| print(summary) |
| run_cmd(("echo %s >> %s" % (summary, report))) |
| print("RTL & ISS regression report is saved to %s" % report) |
| |
| |
| # Parse input arguments |
| parser = argparse.ArgumentParser() |
| |
| parser.add_argument("--o", type=str, default="./out", |
| help="Output directory name") |
| parser.add_argument("--testlist", type=str, default="riscv_dv_extension/testlist.yaml", |
| help="Regression testlist") |
| parser.add_argument("--test", type=str, default="all", |
| help="Test name, 'all' means all tests in the list") |
| parser.add_argument("--seed", type=int, default=-1, |
| help="Randomization seed, default -1 means random seed") |
| parser.add_argument("--iterations", type=int, default=0, |
| help="Override the iteration count in the test list") |
| parser.add_argument("--simulator", type=str, default="vcs", |
| help="Simulator used to run the generator, default VCS") |
| parser.add_argument("--simulator_yaml", type=str, default="yaml/rtl_simulation.yaml", |
| help="RTL simulator setting YAML") |
| parser.add_argument("--iss", type=str, default="spike", |
| help="Instruction set simulator") |
| parser.add_argument("--verbose", type=int, default=0, |
| help="Verbose logging") |
| parser.add_argument("--cmp_opts", type=str, default="", |
| help="Compile options for the generator") |
| parser.add_argument("--sim_opts", type=str, default="", |
| help="Simulation options for the generator") |
| parser.add_argument("--en_cov", type=str, default=0, |
| help="Enable coverage dump") |
| parser.add_argument("--en_wave", type=str, default=0, |
| help="Enable waveform dump") |
| parser.add_argument("--steps", type=str, default="all", |
| help="Run steps: compile,sim,compare") |
| parser.add_argument("--lsf_cmd", type=str, default="", |
| help="LSF command. Run in local sequentially if lsf \ |
| command is not specified") |
| |
| args = parser.parse_args() |
| cwd = os.path.dirname(os.path.realpath(__file__)) |
| |
| # Create the output directory |
| output_dir = ("%s/rtl_sim" % args.o) |
| bin_dir = ("%s/instr_gen/asm_tests" % args.o) |
| subprocess.run(["mkdir", "-p", output_dir]) |
| |
| # Process regression test list |
| matched_list = [] |
| process_regression_list(args.testlist, args.test, |
| args.iterations, matched_list) |
| if len(matched_list) == 0: |
| sys.exit("Cannot find %s in %s" % (args.test, args.testlist)) |
| |
| compile_cmd = [] |
| sim_cmd = "" |
| compile_cmd, sim_cmd = get_simulator_cmd(args.simulator, args.simulator_yaml, |
| args.en_cov, args.en_wave) |
| # Compile TB |
| if args.steps == "all" or re.match("compile", args.steps): |
| rtl_compile(compile_cmd, matched_list, output_dir, |
| args.lsf_cmd, args.cmp_opts, args.verbose) |
| |
| # Run RTL simulation |
| if args.steps == "all" or re.match("sim", args.steps): |
| rtl_sim(sim_cmd, matched_list, output_dir, bin_dir, args.lsf_cmd, |
| args.seed, args.sim_opts, args.verbose) |
| |
| # Compare RTL & ISS simulation result.; |
| if args.steps == "all" or re.match("compare", args.steps): |
| compare(matched_list, args.iss, args.o, args.verbose) |