mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Tidy up the sim.py wrapper script and rename it merge-cov.py
Also, update the copyright notice to be lowRISC: we've rewritten pretty much the entire file, so I don't think it really makes sense to describe it as copyright Google any more. While we're at it, we slightly simplify the plumbing for Xcelium, which means we don't have to export any Xcelium-specific environment variables from the Makefile any more.
This commit is contained in:
parent
f7863ce57c
commit
09cfe83191
4 changed files with 161 additions and 196 deletions
|
@ -33,15 +33,12 @@ SEED := $(shell echo $$RANDOM)
|
|||
# This is the top-level output directory. Everything we generate goes in
|
||||
# here. Most generated stuff actually goes in $(OUT)/seed-$(SEED), which allows
|
||||
# us to run multiple times without deleting existing results.
|
||||
OUT := out
|
||||
export OUT-SEED := $(OUT)/seed-$(SEED)
|
||||
OUT := out
|
||||
OUT-SEED := $(OUT)/seed-$(SEED)
|
||||
|
||||
# Needed for tcl files that are used with Cadence tools.
|
||||
export dv_root := $(realpath ../../../vendor/lowrisc_ip/dv)
|
||||
export DUT_TOP := dut
|
||||
export cov_merge_dir := $(OUT-SEED)/cov_merge
|
||||
export cov_merge_db_dir := $(cov_merge_dir)/merged
|
||||
export cov_report_dir := $(OUT-SEED)/cov_report
|
||||
|
||||
# Enable waveform dumping
|
||||
WAVES := 1
|
||||
|
@ -507,14 +504,15 @@ riscv_dv_fcov: $(metadata)/.cov.gen_fcov.stamp
|
|||
###############################################################################
|
||||
# Merge all output coverage directories into the <out>/rtl_sim directory
|
||||
#
|
||||
# Any coverage databases generated from the riscv_dv_fcov target will be merged as well.
|
||||
# Any coverage databases generated from the riscv_dv_fcov target will be merged
|
||||
# as well.
|
||||
$(metadata)/.cov.merge.stamp: \
|
||||
$(metadata)/.cov.gen_fcov.stamp
|
||||
$(verb)rm -rf $(OUT-DIR)rtl_sim/test.vdb
|
||||
$(verb)./sim.py --steps=cov --simulator="${SIMULATOR}" --o="$(OUT-DIR)"
|
||||
@if [ -d "test.vdb" ]; then \
|
||||
mv -f test.vdb $(OUT-DIR)rtl_sim/; \
|
||||
fi
|
||||
$(metadata)/.cov.gen_fcov.stamp \
|
||||
scripts/merge-cov.py
|
||||
$(verb)scripts/merge-cov.py \
|
||||
$(verb-arg) \
|
||||
--working-dir=$(OUT-SEED) \
|
||||
--simulator=$(SIMULATOR)
|
||||
@ # Bookkeeping
|
||||
@touch $@
|
||||
|
||||
|
|
147
dv/uvm/core_ibex/scripts/merge-cov.py
Executable file
147
dv/uvm/core_ibex/scripts/merge-cov.py
Executable file
|
@ -0,0 +1,147 @@
|
|||
#!/usr/bin/env python3
|
||||
# Copyright lowRISC contributors.
|
||||
# Licensed under the Apache License, Version 2.0, see LICENSE for details.
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
"""Regression script for running the Spike UVM testbench"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
from typing import Set
|
||||
|
||||
from scripts_lib import run_one
|
||||
|
||||
_THIS_DIR = os.path.normpath(os.path.join(os.path.dirname(__file__)))
|
||||
_IBEX_ROOT = os.path.normpath(os.path.join(_THIS_DIR, 4 * '../'))
|
||||
|
||||
|
||||
def find_cov_dirs(start_dir: str, simulator: str) -> Set[str]:
|
||||
assert simulator in ['xlm', 'vcs']
|
||||
|
||||
# For VCS, all generated coverage databases will be named "test.vdb"
|
||||
vdb_dir_name = "test.vdb"
|
||||
|
||||
cov_dirs = set()
|
||||
for path, dirs, files in os.walk(start_dir):
|
||||
for file in files:
|
||||
if file.endswith(".ucd") and simulator == 'xlm':
|
||||
logging.info("Found coverage database (ucd) at %s" % path)
|
||||
cov_dirs.add(path)
|
||||
if vdb_dir_name in dirs and simulator == 'vcs':
|
||||
vdb_path = os.path.join(path, vdb_dir_name)
|
||||
logging.info("Found coverage database (vdb) at %s" % vdb_path)
|
||||
cov_dirs.add(vdb_path)
|
||||
|
||||
return cov_dirs
|
||||
|
||||
|
||||
def merge_cov_vcs(cov_dir: str, verbose: bool, cov_dirs: Set[str]) -> int:
|
||||
logging.info("Generating merged coverage directory")
|
||||
cmd = (['urg', '-full64',
|
||||
'-format', 'both',
|
||||
'-dbname', os.path.join(cov_dir, 'merged.vdb'),
|
||||
'-report', os.path.join(cov_dir, 'report'),
|
||||
'-log', os.path.join(cov_dir, 'merge.log'),
|
||||
'-dir'] +
|
||||
list(cov_dirs))
|
||||
return run_one(verbose, cmd, discard_stdstreams=True)
|
||||
|
||||
|
||||
def merge_cov_xlm(cov_dir: str, verbose: bool, cov_dirs: Set[str]) -> int:
|
||||
xcelium_scripts = os.path.join(_IBEX_ROOT,
|
||||
'vendor/lowrisc_ip/dv/tools/xcelium')
|
||||
|
||||
# The merge TCL code uses a glob to find all available scopes and previous
|
||||
# runs. In order to actually get the databases we need to go up once so
|
||||
# that the "*" finds the directory we've seen.
|
||||
cov_dir_parents = {os.path.normpath(os.path.join(d, '..'))
|
||||
for d in cov_dirs}
|
||||
|
||||
merge_dir = os.path.join(cov_dir, 'merged')
|
||||
report_dir = os.path.join(cov_dir, 'report')
|
||||
|
||||
# Get all needed directories for merge and report stages.
|
||||
xlm_cov_dirs = {
|
||||
'cov_merge_db_dir': merge_dir,
|
||||
'cov_report_dir': report_dir
|
||||
}
|
||||
|
||||
# Finally, set an environment variable containing all the directories that
|
||||
# should be merged (this is how the list gets passed down to the TCL script
|
||||
# that handles them)
|
||||
xlm_cov_dirs['cov_db_dirs'] = ' '.join(cov_dir_parents)
|
||||
|
||||
xlm_env = os.environ | xlm_cov_dirs
|
||||
|
||||
# First do the merge
|
||||
imc_cmd = ["imc", "-64bit", "-licqueue"]
|
||||
cov_merge_tcl = os.path.join(xcelium_scripts, "cov_merge.tcl")
|
||||
merge_ret = run_one(verbose,
|
||||
imc_cmd + ["-exec", cov_merge_tcl,
|
||||
"-logfile", os.path.join(cov_dir,
|
||||
'merge.log'),
|
||||
"-nostdout"],
|
||||
env=xlm_env)
|
||||
if merge_ret:
|
||||
return merge_ret
|
||||
|
||||
# Then do the reporting
|
||||
cov_report_tcl = os.path.join(xcelium_scripts, "cov_report.tcl")
|
||||
os.makedirs(report_dir, exist_ok=True)
|
||||
report_ret = run_one(verbose,
|
||||
imc_cmd + ["-load", merge_dir,
|
||||
"-exec", cov_report_tcl,
|
||||
"-logfile", os.path.join(cov_dir,
|
||||
'report.log'),
|
||||
"-nostdout"],
|
||||
env=xlm_env)
|
||||
return report_ret
|
||||
|
||||
|
||||
def main():
|
||||
'''Entry point when run as a script'''
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("--working-dir")
|
||||
parser.add_argument("--simulator")
|
||||
parser.add_argument("--verbose", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
if args.simulator not in ['xlm', 'vcs']:
|
||||
raise ValueError(f'Unsupported simulator: {args.simulator}.')
|
||||
|
||||
output_dir = os.path.join(args.working_dir, 'coverage')
|
||||
|
||||
# If output_dir exists, delete it: we'll re-generate its contents in a sec
|
||||
# and we don't want to accidentally pick them up as part of the merge.
|
||||
try:
|
||||
shutil.rmtree(output_dir)
|
||||
except FileNotFoundError:
|
||||
pass
|
||||
|
||||
# Now make output_dir again (but empty)
|
||||
os.makedirs(output_dir)
|
||||
|
||||
# Compile a list of all directories that contain coverage databases
|
||||
cov_dirs = find_cov_dirs(args.working_dir, args.simulator)
|
||||
if not cov_dirs:
|
||||
logging.info(f"No coverage found for {args.simulator}.")
|
||||
return 1
|
||||
|
||||
merge_funs = {
|
||||
'vcs': merge_cov_vcs,
|
||||
'xlm': merge_cov_xlm
|
||||
}
|
||||
return merge_funs[args.simulator](output_dir, args.verbose, cov_dirs)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main())
|
||||
except RuntimeError as err:
|
||||
sys.stderr.write('Error: {}\n'.format(err))
|
||||
sys.exit(1)
|
|
@ -17,7 +17,8 @@ RISCV_DV_ROOT = os.path.normpath(os.path.join(IBEX_ROOT,
|
|||
|
||||
def run_one(verbose: bool,
|
||||
cmd: List[str],
|
||||
discard_stdstreams: bool = False) -> int:
|
||||
discard_stdstreams: bool = False,
|
||||
env: Dict[str, str] = None) -> int:
|
||||
'''Run a command, returning its return code
|
||||
|
||||
If verbose is true, print the command to stderr first (a bit like bash -x).
|
||||
|
@ -45,7 +46,8 @@ def run_one(verbose: bool,
|
|||
return subprocess.run(cmd,
|
||||
stdout=stdstream_dest,
|
||||
stderr=stdstream_dest,
|
||||
close_fds=False).returncode
|
||||
close_fds=False,
|
||||
env=env).returncode
|
||||
|
||||
|
||||
def start_riscv_dv_run_cmd(verbose: bool):
|
||||
|
|
|
@ -1,182 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# 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 running the Spike UVM testbench"""
|
||||
|
||||
import argparse
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
_CORE_IBEX = os.path.normpath(os.path.join(os.path.dirname(__file__)))
|
||||
_IBEX_ROOT = os.path.normpath(os.path.join(_CORE_IBEX, '../../..'))
|
||||
_RISCV_DV_ROOT = os.path.join(_IBEX_ROOT, 'vendor/google_riscv-dv')
|
||||
_XCELIUM_SCRIPTS = os.path.join(_IBEX_ROOT, 'vendor/lowrisc_ip/dv/tools/xcelium')
|
||||
_OLD_SYS_PATH = sys.path
|
||||
|
||||
# Import riscv_trace_csv and lib from _DV_SCRIPTS before putting sys.path back
|
||||
# as it started.
|
||||
try:
|
||||
sys.path = ([os.path.join(_CORE_IBEX, 'riscv_dv_extension'),
|
||||
os.path.join(_IBEX_ROOT, 'util'),
|
||||
os.path.join(_RISCV_DV_ROOT, 'scripts')] +
|
||||
sys.path)
|
||||
|
||||
from lib import run_cmd, setup_logging, RET_SUCCESS, RET_FAIL
|
||||
|
||||
|
||||
finally:
|
||||
sys.path = _OLD_SYS_PATH
|
||||
|
||||
|
||||
#TODO(udinator) - support DSim, and Riviera
|
||||
def gen_cov(base_dir, simulator, lsf_cmd):
|
||||
"""Generate a merged coverage directory.
|
||||
|
||||
Args:
|
||||
base_dir: the base simulation output directory (default: out/)
|
||||
simulator: the chosen RTL simulator
|
||||
lsf_cmd: command to run on LSF
|
||||
|
||||
"""
|
||||
# Compile a list of all output seed-###/rtl_sim/test.vdb directories
|
||||
dir_list = []
|
||||
|
||||
# All generated coverage databases will be named "test.vdb"
|
||||
vdb_dir_name = "test.vdb"
|
||||
|
||||
# Copy our existing environment variables to find our unique out folder.
|
||||
my_env = os.environ.copy()
|
||||
for path, dirs, files in os.walk(my_env['OUT-SEED']):
|
||||
for file in files:
|
||||
if file.endswith(".ucd") and simulator == 'xlm':
|
||||
logging.info("Found coverage database (ucd) at %s" % path)
|
||||
dir_list.append(path)
|
||||
if vdb_dir_name in dirs and simulator == 'vcs':
|
||||
vdb_path = os.path.join(path, vdb_dir_name)
|
||||
logging.info("Found coverage database (vdb) at %s" % vdb_path)
|
||||
dir_list.append(vdb_path)
|
||||
|
||||
if dir_list == []:
|
||||
logging.info(f"No coverage data available for {simulator}, exiting...")
|
||||
sys.exit(RET_SUCCESS)
|
||||
|
||||
if simulator == 'vcs':
|
||||
cov_cmd = "urg -full64 -format both -dbname test.vdb " \
|
||||
"-report %s/rtl_sim/urgReport -dir" % base_dir
|
||||
for cov_dir in dir_list:
|
||||
cov_cmd += " %s" % cov_dir
|
||||
logging.info("Generating merged coverage directory")
|
||||
if lsf_cmd is not None:
|
||||
cov_cmd = lsf_cmd + ' ' + cov_cmd
|
||||
run_cmd(cov_cmd)
|
||||
elif simulator == 'xlm':
|
||||
# Get all needed directories for merge and report stages.
|
||||
cov_dirs = {
|
||||
'cov_merge_dir': my_env['cov_merge_dir'],
|
||||
'cov_merge_db_dir': my_env['cov_merge_db_dir'],
|
||||
'cov_report_dir': my_env['cov_report_dir'],
|
||||
}
|
||||
|
||||
# Create the mentioned directories.
|
||||
for d in cov_dirs.values():
|
||||
os.makedirs(d, exist_ok=True)
|
||||
|
||||
# The merge TCL code uses a glob to find all available scopes and
|
||||
# previous runs. In order to actually get the databases we need to
|
||||
# go up once so that the "*" finds the directory we've seen.
|
||||
cov_dirs['cov_db_dirs'] = ' '.join([os.path.join(d, '..')
|
||||
for d in dir_list])
|
||||
my_env.update(cov_dirs)
|
||||
|
||||
# First do the merge
|
||||
imc_cmd = ["imc", "-64bit", "-licqueue"]
|
||||
cov_merge_tcl = os.path.join(_XCELIUM_SCRIPTS, "cov_merge.tcl")
|
||||
subprocess.run(imc_cmd + ["-exec", cov_merge_tcl],
|
||||
env=my_env)
|
||||
# Then do the reporting
|
||||
cov_report_tcl = os.path.join(_XCELIUM_SCRIPTS, "cov_report.tcl")
|
||||
subprocess.run(imc_cmd + ["-load", cov_dirs['cov_merge_db_dir'],
|
||||
"-exec", cov_report_tcl],
|
||||
env=my_env)
|
||||
else:
|
||||
logging.error("%s is an unsuported simulator! Exiting..." % simulator)
|
||||
sys.exit(RET_FAIL)
|
||||
|
||||
|
||||
def main():
|
||||
'''Entry point when run as a script'''
|
||||
|
||||
# Parse input arguments
|
||||
parser = argparse.ArgumentParser()
|
||||
|
||||
parser.add_argument("--o", type=str, default="out",
|
||||
help="Output directory name")
|
||||
parser.add_argument("--simulator", type=str, default="vcs",
|
||||
help="RTL simulator to use (default: vcs)")
|
||||
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
|
||||
help="Verbose logging")
|
||||
parser.add_argument("--en_cov", action='store_true',
|
||||
help="Enable coverage dump")
|
||||
parser.add_argument("--en_wave", action='store_true',
|
||||
help="Enable waveform dump")
|
||||
parser.add_argument("--en_cosim", action='store_true',
|
||||
help="Enable cosimulation")
|
||||
parser.add_argument("--steps", type=str, default="all",
|
||||
help="Run steps: compile,cov")
|
||||
parser.add_argument("--lsf_cmd", type=str,
|
||||
help=("LSF command. Run locally if lsf "
|
||||
"command is not specified"))
|
||||
|
||||
args = parser.parse_args()
|
||||
setup_logging(args.verbose)
|
||||
|
||||
# If args.lsf_cmd is an empty string return an error message and exit from
|
||||
# the script, as doing nothing will result in arbitrary simulation timeouts
|
||||
# and errors later on in the run flow.
|
||||
if args.lsf_cmd == "":
|
||||
logging.error("The LSF command passed in is an empty string.")
|
||||
return RET_FAIL
|
||||
|
||||
# Create the output directory
|
||||
output_dir = ("%s/rtl_sim" % args.o)
|
||||
subprocess.run(["mkdir", "-p", output_dir])
|
||||
|
||||
steps = {
|
||||
'compile': args.steps == "all" or 'compile' in args.steps,
|
||||
'cov': args.steps == "all" or 'cov' in args.steps
|
||||
}
|
||||
|
||||
# Compile TB
|
||||
if steps['compile']:
|
||||
raise RuntimeError('TB compilation is no longer supported by this '
|
||||
'script: use compile-tb.py.')
|
||||
exit(1)
|
||||
|
||||
# Generate merged coverage directory and load it into appropriate GUI
|
||||
if steps['cov']:
|
||||
gen_cov(args.o, args.simulator, args.lsf_cmd)
|
||||
|
||||
return RET_SUCCESS
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
try:
|
||||
sys.exit(main())
|
||||
except RuntimeError as err:
|
||||
sys.stderr.write('Error: {}\n'.format(err))
|
||||
sys.exit(RET_FAIL)
|
Loading…
Add table
Add a link
Reference in a new issue