Update google_riscv-dv to google/riscv-dv@4b333ba (#462)

Update code from upstream repository https://github.com/google/riscv-
dv to revision 4b333ba1ef285ec4508c606efa64610136154a5e

* cg instantion based on supported_isa (google/riscv-dv#303)
  (udinator)
* Fix coverage collection issue, change default target to rv32imc
  (google/riscv-dv#302) (taoliug)
* Integrate whisper(swerv-ISS) (google/riscv-dv#301) (taoliug)
* Fix cov.py, set UVM_VERBOSITY to UVM_HIGH for verbose mode
  (google/riscv-dv#299) (taoliug)
* Fix jalr handling issue for ovpsim (google/riscv-dv#298) (taoliug)
* Add noclean option, change default output directory of coverage
  collection (google/riscv-dv#297) (taoliug)
* Enable using core trace logs for coverage collection (google/riscv-
  dv#291) (udinator)
* Fix isa/mabi setup issue for RV64GC target (google/riscv-dv#296)
  (taoliug)
* fixed line widths (x2) and check error returns for any questa
  simalator (google/riscv-dv#293) (simond-imperas)
* Unknown instruction fix (google/riscv-dv#290) (simond-imperas)
* Fix ovpsim log process issue (google/riscv-dv#289) (udinator)
* adding riscvOVPsim vector instruction trace to csv processing -
  start (3rd Attempt) (google/riscv-dv#288) (simond-imperas)

Signed-off-by: Udi <udij@google.com>
This commit is contained in:
udinator 2019-11-12 14:39:22 -08:00 committed by GitHub
parent 1cc4831480
commit 6ce8b6ecf2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 579 additions and 183 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: 44bec7695fd2399166e181fa84b66a608b5f745f
rev: 4b333ba1ef285ec4508c606efa64610136154a5e
}
}

View file

@ -1,6 +1,6 @@
*.ini
*.bin
out_*/
*out_*/
work/
*.pyc
/.cproject

View file

@ -65,12 +65,18 @@ one of below to run ISS simulation.
- [riscv-ovpsim](https://github.com/riscv/riscv-ovpsim) setup
- Download the riscv-ovpsim binary
- Set environment variable OVPSIM_PATH to the directory of the ovpsim binary
- [whisper(swerv-ISS)](https://github.com/westerndigitalcorporation/swerv-ISS) setup
- Follow the instruction to install the ISS, and set WHISPER_ISS to the
whisper binary
- [sail-riscv](https://github.com/rems-project/sail-riscv) setup
- Follow the [steps](https://github.com/rems-project/sail-riscv/blob/master/README.md) to install sail-riscv
- Set environment variable SAIL_RISCV to the sail-riscv binary
```bash
export SPIKE_PATH=$RISCV_TOOLCHAIN/bin
export SAIL_RISCV="xx/xxx/riscv_ocaml_sim_RV64"
export OVPSIM_PATH=/xx/xxx/riscv-ovpsim/bin/Linux64
export WHISPER_ISS="xx/xxx/swerv-ISS/build-Linux/whisper"
```
## Running the generator
@ -144,6 +150,9 @@ python3 run.py --test riscv_arithmetic_basic_test --iss spike
# Run ISS with riscv-ovpsim
python3 run.py --test riscv_rand_instr_test --iss ovpsim
# Run ISS with whisper (swerv-ISS)
python3 run.py --test riscv_rand_instr_test --iss whisper
# Run ISS with sail-riscv
python3 run.py --test riscv_rand_instr_test --iss sail
```
@ -163,6 +172,7 @@ real RISC-V processor.
```bash
python3 run.py --test=riscv_rand_instr_test --iss=spike,ovpsim
python3 run.py --test=riscv_rand_instr_test --iss=ovpsim,whisper
python3 run.py --test=riscv_rand_instr_test --iss=spike,sail
```
@ -459,11 +469,11 @@ For more information, detailed documentation of this mechanism can be found in
## Functional coverage (work in progress)
This flow extracts functional coverage information from the
instruction trace generated by ISS. It's indepdent of the instruction generation
instruction trace generated by ISS. It's independent of the instruction generation
flow and does not require a tracer implementation in the RTL. You can use this
flow as long as your program can be run with the ISS supported in this flow. The
flow as long as your program can be run with an ISS supported in this flow. The
flow parses the instruction trace log and converts it to a CSV trace format. After
that, a SV test will be run to process the CSV trace files and sample functional
that, a SV test is run to process the CSV trace files and sample functional
coverage from there.
The functional covergroup is defined in [riscv_instr_cover_group.sv](https://github.com/google/riscv-dv/blob/master/src/riscv_instr_cover_group.sv). It includes below major categories:
@ -476,13 +486,13 @@ The functional covergroup is defined in [riscv_instr_cover_group.sv](https://git
- Hint instruction
- Illegal instruction
- All compressed and non-compressed opcode
- Access to all implemened privieleged CSR
- Access to all implemened privileged CSR
- Exception and interrupt
The functional covergroup is still under active development. Please feel free to
add anything you are interested or file a bug for any feature request.
Before start, please check the you have modified [riscv_core_setting.sv](https://github.com/google/riscv-dv/blob/master/setting/riscv_core_setting.sv) to reflect your processor capabilities. The covergroup is selectively instantiated based on this setting so that you don't need to deal with excluding unrelated coverpoints later. You also need to get the Spike ISS setup before running this flow.
Before start, please check the you have modified [riscv_core_setting.sv](https://github.com/google/riscv-dv/blob/master/setting/riscv_core_setting.sv) to reflect your processor capabilities. The covergroup is selectively instantiated based on this setting so that you don't need to deal with excluding unrelated coverpoints later. You also need to get the Spike ISS or riscvOVPsim ISS (riscv-ovpsim) setup before running this flow.
```bash

View file

@ -31,31 +31,44 @@ from scripts.sail_log_to_trace_csv import *
LOGGER = logging.getLogger()
def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, \
opts, timeout, simulator, simulator_yaml, custom_target, target):
def collect_cov(log_dir, out, core, iss, testlist, batch_size, lsf_cmd, steps, \
opts, timeout, simulator, simulator_yaml, custom_target, \
isa, target, stop_on_first_error):
"""Collect functional coverage from the instruction trace
Args:
log_dir : ISS log directory
out : Output directory
iss : Instruction set simulator
test_list : Testlist of the coverage test
batch_size : Number of trace CSV to process per test
lsf_cmd : LSF command used to run the instruction generator
steps : csv:log to CSV, cov:sample coverage
opts : Additional options to the instruction generator
timeout : Timeout limit in seconds
simulator : RTL simulator used to run
simulator_yaml : RTL simulator configuration file in YAML format
custom_target : Path for the custom target dir
target : Predefined target
log_dir : Trace log directory
out : Output directory
core : RISC-V core to be used for analysis
iss : Instruction set simulator
testlist : Testlist of the coverage test
batch_size : Number of trace CSV to process per test
lsf_cmd : LSF command used to run the instruction generator
steps : csv:log to CSV, cov:sample coverage
opts : Additional options to the instruction generator
timeout : Timeout limit in seconds
simulator : RTL simulator used to run
simulator_yaml : RTL simulator configuration file in YAML format
custom_target : Path for the custom target dir
isa : RISC-V ISA variant
target : Predefined target
stop_on_first_error : will end run on first error detected
"""
cwd = os.path.dirname(os.path.realpath(__file__))
log_list = []
csv_list = []
trace_log = ("%s/%s_trace_log" % (out, iss))
logging.info("Processing trace log under %s" % log_dir)
run_cmd("find %s -name \"*.log\" | sort > %s" % (log_dir, trace_log))
if core:
"""
If functional coverage is being collected from an RTL core implementation,
the flow assumes that the core's trace logs have already been converted to
CSV files by the post_compare step of the flow.
"""
trace_log = ("%s/%s_trace_log" % (out, core))
run_cmd("find %s -name \"*.csv\" | sort > %s" % (log_dir, trace_log))
else:
trace_log = ("%s/%s_trace_log" % (out, iss))
run_cmd("find %s -name \"*.log\" | sort > %s" % (log_dir, trace_log))
with open(trace_log) as f:
for line in f:
line = line.rstrip()
@ -66,14 +79,18 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, \
for i in range(len(log_list)):
log = log_list[i]
csv = csv_list[i]
logging.info("Process %0s log[%0d/%0d] : %s" % (iss, i+1, len(log_list), log))
if iss == "spike":
process_spike_sim_log(log, csv, 1)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv, 1)
# If a core target is defined, prioritize over ISS
if core:
logging.info("Process %0s log[%0d/%0d] : %s" % (core, i+1, len(log_list), log))
else:
logging.error("Full trace for %s is not supported yet" % iss)
sys.exit(1)
logging.info("Process %0s log[%0d/%0d] : %s" % (iss, i+1, len(log_list), log))
if iss == "spike":
process_spike_sim_log(log, csv, 1)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv, 1, stop_on_first_error)
else:
logging.error("Full trace for %s is not supported yet" % iss)
sys.exit(1)
if steps == "all" or re.match("cov", steps):
build_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
" --co -o %s --cov -tl %s %s " %
@ -91,7 +108,8 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, \
if custom_target:
base_sim_cmd += (" --custom_target %s" % custom_target)
logging.info("Building the coverage collection framework")
run_cmd(build_cmd)
output = run_cmd(build_cmd)
check_simulator_return(output, simulator, stop_on_first_error)
file_idx = 0
trace_idx = 0
trace_csv_opts = ""
@ -115,6 +133,7 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, \
if lsf_cmd == "":
logging.info("Processing batch %0d/%0d" % (file_idx+1, batch_cnt))
run_cmd(sim_cmd)
check_simulator_return(output, simulator, stop_on_first_error)
else:
sim_cmd += (" --lsf_cmd \"%s\"" % lsf_cmd)
sim_cmd_list.append(sim_cmd)
@ -125,7 +144,7 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, \
def run_cov_debug_test(out, instr_cnt, testlist, batch_size, opts, lsf_cmd,\
timeout, simulator, simulator_yaml, custom_target, target):
timeout, simulator, simulator_yaml, custom_target, isa, target):
"""Collect functional coverage from the instruction trace
Args:
@ -139,6 +158,7 @@ def run_cov_debug_test(out, instr_cnt, testlist, batch_size, opts, lsf_cmd,\
simulator : RTL simulator used to run
simulator_yaml : RTL simulator configuration file in YAML format
custom_target : Path for the custom target dir
isa : RISC-V ISA variant
target : Predefined target
"""
cwd = os.path.dirname(os.path.realpath(__file__))
@ -153,10 +173,10 @@ def run_cov_debug_test(out, instr_cnt, testlist, batch_size, opts, lsf_cmd,\
build_cmd += (" --custom_target %s" % custom_target)
run_cmd(build_cmd)
base_sim_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
"--so -o %s --cov -tl %s %s "
"--so -o %s --cov -tl %s --isa %s%s "
"-tn riscv_instr_cov_debug_test --steps gen "
"--sim_opts \"+num_of_iterations=<instr_cnt>\"" %
(cwd, simulator, simulator_yaml, out, testlist, opts))
(cwd, simulator, simulator_yaml, out, testlist, isa, opts))
if target:
base_sim_cmd += (" --target %s" % target)
if custom_target:
@ -200,7 +220,7 @@ def setup_parser():
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
help="Verbose logging")
parser.add_argument("--dir", type=str,
help="Directory of ISS log")
help="Directory of trace log files")
parser.add_argument("-bz", "--batch_size", dest="batch_size", type=int, default=0,
help="Number of CSV to process per run")
parser.add_argument("-d", "--debug_mode", dest="debug_mode", action="store_true",
@ -211,6 +231,10 @@ def setup_parser():
help="Number of CSV to process per run")
parser.add_argument("-s", "--steps", type=str, default="all",
help="Run steps: csv,cov", dest="steps")
parser.add_argument("--core", type=str, default="",
help="Name of target core")
parser.add_argument("--isa", type=str, default="",
help="RISC-V ISA variant")
parser.add_argument("--iss", type=str, default="spike",
help="RISC-V instruction set simulator: spike,ovpsim,sail")
parser.add_argument("-tl", "--testlist", type=str, default="",
@ -224,15 +248,21 @@ def setup_parser():
help="Run the generator with pre-defined targets: \
rv32imc, rv32i, rv64imc, rv64gc")
parser.add_argument("-si", "--simulator", type=str, default="vcs",
help="Simulator used to run the generator, default VCS", dest="simulator")
help="Simulator used to run the generator, \
default VCS", dest="simulator")
parser.add_argument("--simulator_yaml", type=str, default="",
help="RTL simulator setting YAML")
parser.add_argument("-ct", "--custom_target", type=str, default="",
help="Directory name of the custom target")
parser.add_argument("-cs", "--core_setting_dir", type=str, default="",
help="Path for the riscv_core_setting.sv")
parser.add_argument("--stop_on_first_error", dest="stop_on_first_error",
action="store_true", help="Stop on detecting first error")
parser.set_defaults(verbose=False)
parser.set_defaults(debug_mode=False)
parser.set_defaults(stop_on_first_error=False)
parser.add_argument("--noclean", action="store_true",
help="Do not clean the output of the previous runs")
return parser
def main():
@ -255,6 +285,10 @@ def main():
if args.debug_mode:
args.target = "rv64gc"
# Disable ISS coverage if a core is passed in
if args.core:
args.iss = ""
# Keep the core_setting_dir option to be backward compatible, suggest to use
# --custom_target
if args.core_setting_dir:
@ -265,25 +299,31 @@ def main():
if not args.custom_target:
args.core_setting_dir = cwd + "/target/"+ args.target
else:
args.testlist = args.custom_target + "/testlist.yaml"
args.testlist = cwd + "/yaml/cov_testlist.yaml" ## needed if need to force
# Create output directory
if args.o is None:
output_dir = "out_" + str(date.today())
output_dir = "cov_out_" + str(date.today())
else:
output_dir = args.o
if args.noclean is False:
os.system("rm -rf %s" % output_dir)
subprocess.run(["mkdir", "-p", output_dir])
if args.debug_mode:
run_cov_debug_test(output_dir, args.instr_cnt, args.testlist,
args.batch_size, args.opts, args.lsf_cmd, args.timeout,
args.simulator, args.simulator_yaml, args.custom_target, args.target)
args.simulator, args.simulator_yaml, args.custom_target,
args.isa, args.target)
else:
collect_cov(args.dir, output_dir, args.iss, args.testlist, args.batch_size,
args.lsf_cmd, args.steps, args.opts, args.timeout,
args.simulator, args.simulator_yaml, args.custom_target, args.target)
collect_cov(args.dir, output_dir, args.core, args.iss, args.testlist,
args.batch_size, args.lsf_cmd, args.steps, args.opts, args.timeout,
args.simulator, args.simulator_yaml, args.custom_target,
args.isa, args.target, args.stop_on_first_error)
logging.info("Coverage results are saved to %s" % output_dir)
if __name__ == "__main__":
main()

View file

@ -27,6 +27,7 @@ from datetime import date
from scripts.lib import *
from scripts.spike_log_to_trace_csv import *
from scripts.ovpsim_log_to_trace_csv import *
from scripts.whisper_log_trace_csv import *
from scripts.sail_log_to_trace_csv import *
from scripts.instr_trace_compare import *
@ -95,6 +96,16 @@ def parse_iss_yaml(iss, iss_yaml, isa, setting_dir):
cmd = re.sub("\<path_var\>", get_env_var(entry['path_var']), cmd)
if iss == "ovpsim":
cmd = re.sub("\<cfg_path\>", setting_dir, cmd)
elif iss == "whisper":
m = re.search(r"rv(?P<xlen>[0-9]+?)(?P<variant>[a-z]+?)$", isa)
if m:
# TODO: Support u/s mode
cmd = re.sub("\<xlen\>", m.group('xlen'), cmd)
variant = re.sub('g', 'imafd', m.group('variant'))
cmd = re.sub("\<variant\>", variant, cmd)
else:
logging.error("Illegal ISA %0s" % isa)
cmd = re.sub("\<xlen\>", setting_dir, cmd)
else:
cmd = re.sub("\<variant\>", isa, cmd)
return cmd
@ -121,7 +132,7 @@ def get_iss_cmd(base_cmd, elf, log):
def gen(test_list, csr_file, end_signature_addr, isa, simulator,
simulator_yaml, output_dir, sim_only, compile_only, lsf_cmd, seed,
cwd, cmp_opts, sim_opts, timeout_s, core_setting_dir, ext_dir, cov,
log_suffix, batch_size, seed_yaml):
log_suffix, batch_size, seed_yaml, stop_on_first_error, verbose=False):
"""Run the instruction generator
Args:
@ -145,6 +156,7 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
log_suffix : Simulation log file name suffix
batch_size : Number of tests to generate per run
seed_yaml : Seed specification from a prior regression
stop_on_first_error : will end run on first error detected
"""
# Mutually exclusive options between compile_only and sim_only
if compile_only and sim_only:
@ -170,7 +182,9 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
cmd = re.sub("<cmp_opts>", cmp_opts, cmd)
logging.debug("Compile command: %s" % cmd)
logging.debug(run_cmd(cmd))
output = run_cmd(cmd)
logging.debug(output)
check_simulator_return(output, simulator, stop_on_first_error)
# Run the instruction generator
if not compile_only:
cmd_list = []
@ -201,7 +215,8 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
if lsf_cmd:
cmd_list.append(cmd)
else:
run_cmd(cmd, timeout_s)
output = run_cmd(cmd, timeout_s)
check_simulator_return(output, simulator, stop_on_first_error)
else:
if batch_size > 0:
batch_cnt = int((iterations + batch_size - 1) / batch_size);
@ -224,6 +239,8 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
(" +start_idx=%d " % (i*batch_size)) + \
(" +asm_file_name=%s/asm_tests/%s " % (output_dir, test['test'])) + \
(" -l %s/sim_%s_%d%s.log " % (output_dir, test['test'], i, log_suffix))
if verbose:
cmd += "+UVM_VERBOSITY=UVM_HIGH "
cmd = re.sub("<seed>", str(rand_seed), cmd)
sim_seed[test_id] = str(rand_seed)
if "gen_opts" in test:
@ -235,7 +252,8 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
else:
logging.info("Running %s, batch %0d/%0d, test_cnt:%0d" %
(test['test'], i+1, batch_cnt, test_cnt))
run_cmd(cmd, timeout_s)
output = run_cmd(cmd, timeout_s)
check_simulator_return(output, simulator, stop_on_first_error)
if sim_seed:
with open(('%s/seed.yaml' % os.path.abspath(output_dir)) , 'w') as outfile:
yaml.dump(sim_seed, outfile, default_flow_style=False)
@ -362,7 +380,7 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, isa, setting_dir, timeout
logging.debug(cmd)
def iss_cmp(test_list, iss, output_dir, isa):
def iss_cmp(test_list, iss, output_dir, isa, stop_on_first_error):
"""Compare ISS simulation reult
Args:
@ -370,6 +388,7 @@ def iss_cmp(test_list, iss, output_dir, isa):
iss : List of instruction set simulators
output_dir : Output directory of the ELF files
isa : ISA
stop_on_first_error : will end run on first error detected
"""
iss_list = iss.split(",")
if len(iss_list) != 2:
@ -390,9 +409,11 @@ def iss_cmp(test_list, iss, output_dir, isa):
if iss == "spike":
process_spike_sim_log(log, csv)
elif iss == "ovpsim":
process_ovpsim_sim_log(log, csv)
process_ovpsim_sim_log(log, csv, 1, stop_on_first_error)
elif iss == "sail":
process_sail_sim_log(log, csv)
elif iss == "whisper":
process_whisper_sim_log(log, csv)
else:
logging.error("Unsupported ISS" % iss)
sys.exit(1)
@ -413,7 +434,7 @@ def setup_parser():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--target", type=str, default="rv64imc",
parser.add_argument("--target", type=str, default="rv32imc",
help="Run the generator with pre-defined targets: \
rv32imc, rv32i, rv64imc, rv64gc")
parser.add_argument("-o", "--output", type=str,
@ -481,10 +502,13 @@ def setup_parser():
parser.add_argument("-bz", "--batch_size", type=int, default=0,
help="Number of tests to generate per run. You can split a big"
" job to small batches with this option")
parser.add_argument("--stop_on_first_error", dest="stop_on_first_error", action="store_true",
help="Stop on detecting first error")
parser.set_defaults(co=False)
parser.set_defaults(so=False)
parser.set_defaults(verbose=False)
parser.set_defaults(cov=False)
parser.set_defaults(stop_on_first_error=False)
return parser
@ -526,14 +550,18 @@ def main():
elif args.target == "rv64imc":
args.mabi = "lp64"
args.isa = "rv64imc"
elif args.target == "rv64gc":
args.mabi = "lp64"
args.isa = "rv64gc"
elif args.target == "ml":
args.mabi = "lp64"
args.isa = "rv64imc"
else:
print ("Unsupported pre-defined target: %0s" % args.target)
else:
if (not args.mabi) or (not args.isa):
sys.exit("mabi and isa must be specified for custom target %0s" % args.custom_target)
if re.match(".*gcc_compile.*", args.steps) or re.match(".*iss_sim.*", args.steps):
if (not args.mabi) or (not args.isa):
sys.exit("mabi and isa must be specified for custom target %0s" % args.custom_target)
if not args.testlist:
args.testlist = args.custom_target + "/testlist.yaml"
@ -565,7 +593,7 @@ def main():
args.co, args.lsf_cmd, args.seed, cwd, args.cmp_opts,
args.sim_opts, args.gen_timeout, args.core_setting_dir,
args.user_extension_dir, args.cov, args.log_suffix, args.batch_size,
args.seed_yaml)
args.seed_yaml, args.stop_on_first_error, args.verbose)
if not args.co:
# Compile the assembly program to ELF, convert to plain binary
@ -579,7 +607,7 @@ def main():
# Compare ISS simulation result
if args.steps == "all" or re.match(".*iss_cmp.*", args.steps):
iss_cmp(matched_list, args.iss, output_dir, args.isa)
iss_cmp(matched_list, args.iss, output_dir, args.isa, args.stop_on_first_error)
if __name__ == "__main__":
main()

View file

@ -172,3 +172,21 @@ def process_regression_list(testlist, test, iterations, matched_list):
logging.info("Found matched tests: %s, iterations:%0d" %
(entry['test'], entry['iterations']))
matched_list.append(entry)
def check_simulator_return(output, simulator, stop_on_first_error):
"""
tests simulator output for errors and terminates run if found
ONLY works when verbose is on (as output not returned otherwise)
TODO add other simulators
"""
if not stop_on_first_error: return
if "questa" in simulator:
for line in output.splitlines():
if "Errors: " in line:
if not "Errors: 0" in line:
logging.fatal (
"check_simulator_return (%s): TERMINATING as got errors: [%s]" %
(simulator, line))
sys.exit(-1)

View file

@ -1,5 +1,6 @@
"""
Copyright 2019 Google LLC
Copyright 2019 Imperas Software Ltd
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -13,7 +14,7 @@ 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.
Convert ovpsim sim log to standard riscv instruction trace format
Convert ovpsim sim log to standard riscv-dv .csv instruction trace format
"""
import re
import os
@ -21,29 +22,62 @@ import argparse
import logging
import sys
import pprint as pp
from lib import *
from riscv_trace_csv import *
def convert_mode(pri, line):
if "Machine" in pri: return str(3)
logging.info("convert_mode = UNKNOWN PRIV MODE [%s]: %s" % (pri, line))
try:
from ovpsim_log_to_trace_csv_vectors import *
except:
def assign_operand_vector(a,b,c,d):
""" stub version when no vector processing included """
logging.info("No OVPsim vector instruction processing included")
if stop_on_first_error:
sys.exit(-1)
stop_on_first_error = 0
def fatal (s):
""" ensure we end if a problem """
logging.fatal("ERROR: "+s)
sys.exit(-1)
def convert_mode(pri, line):
""" OVPsim uses text string, convert to numeric """
if "Machine" in pri: return str(3)
if "Supervisor" in pri: return str(1)
if "User" in pri: return str(0)
logging.error("convert_mode = UNKNOWN PRIV MODE [%s]: %s" % (pri, line))
if stop_on_first_error:
sys.exit(-1)
REGS = ["zero","ra","sp","gp","tp","t0","t1","t2","s0","s1",
"a0","a1","a2","a3","a4","a5","a6","a7",
"s2","s3","s4","s5","s6","s7","s8","s9","s10","s11",
"t3","t4","t5","t6"]
def process_jal(trace, operands, gpr):
""" correctly process jal """
# TODO need to merge with jalr
## jal rd, imm
if len(operands) == 2:
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
trace.imm = get_imm_hex_val(operands[1])
else:
fatal("process_jal(%s) wrong num operands (%d)" %
(trace.instr, len(operands)))
def process_jalr(trace, operands, gpr):
""" process jalr """
## jalr x3
## jalr 9(x3)
## jalr x2,x3
## jalr x2,4(x3)
if len(operands) == 1:
trace.rd = 'zero'
trace.rd_val = '0'
m = ADDR_RE.search(operands[0])
trace.rd = 'ra'
trace.rd_val = gpr['ra']
if m: # jalr 9(x3)
trace.rs1 = m.group('rs1')
trace.rs1_val = gpr[trace.rs1]
@ -66,9 +100,11 @@ def process_jalr(trace, operands, gpr):
trace.imm = get_imm_hex_val('0')
def process_if_compressed(prev_trace):
""" convert naming for compressed instructions """
if len(prev_trace.binary) == 4: # compressed are always 4 hex digits
prev_trace.instr = "c."+prev_trace.instr
# logging.debug("process_if_compressed(%s, %s)" % (prev_trace.instr, prev_trace.binary))
# logging.debug("process_if_compressed(%s, %s)" %
# (prev_trace.instr, prev_trace.binary))
pseudos={
'mv' :'addi',
@ -96,7 +132,9 @@ pseudos={
'j' :'jal',
'jr' :'jal',
}
def check_conversion(entry, stop_on_first_error):
def check_conversion(entry):
""" after conversion check that the entry was converted correctly """
instr_str_0 =entry.instr_str.split(" ")[0]
instr =entry.instr.split(" ")[0]
if "c." in instr[0:2]:
@ -108,152 +146,271 @@ def check_conversion(entry, stop_on_first_error):
p_instr = pseudos[instr_str_0]
if p_instr in instr:
return # is pseudo, converted ok
logging.error("converted %10s -> %s <<-- not correct pseudo (%s)" % (instr_str_0, instr, p_instr))
logging.error(
"converted %10s -> %s <<-- not correct pseudo (%s)" %
(instr_str_0, instr, p_instr))
if stop_on_first_error:
sys.exit(-1)
logging.error("converted %10s -> %s <<-- not correct at all" % (instr_str_0, instr))
logging.error("converted %10s -> %s <<-- not correct at all" %
(instr_str_0, instr))
if stop_on_first_error:
sys.exit(-1)
def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 0, stop_on_first_error = 1):
def show_line_instr(line, i):
""" show line """
if i.instr_str[0] in ['v']:
logging.debug("%s" % (line.strip()))
logging.debug(
" -->> instr_str(%s) binary(%s) addr(%s) mode(%s) instr(%s)"
% ( i.instr_str, i.binary, i.addr, i.privileged_mode,
i.instr))
def check_num_operands(instr_str, num_operands, n):
""" ensure consistency """
if n != num_operands:
fatal("%s: num operands wrong, expected (%d) got (%d)" % (instr_str,
n, num_operands))
def is_csr(r):
""" see if r is a csr """
# add more as needed
if r in ["mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch","mcause",
"mtval","vl","vtype"]:
return True
else:
return False
def process_branch_offset (opn, operands, prev_trace):
""" convert from ovpsim logs branch offsets as absolute to relative """
addr = operands[opn]
pc = prev_trace.addr
offset_dec = int(addr, 16) - int(pc, 16)
offset = hex(offset_dec)
operands[opn] = offset
def process_ovpsim_sim_log(ovpsim_log, csv, full_trace = 1, stop = 0,
dont_truncate_after_first_ecall = 0,
verbose2 = False):
"""Process OVPsim simulation log.
Extract instruction and affected register information from ovpsim simulation
log and save to a list.
"""
logging.info("Processing ovpsim log [%d]: %s" % (full_trace, ovpsim_log))
instr_cnt = 0
trace_instr = ""
trace_bin = ""
trace_addr = ""
stop_on_first_error = stop
logging.info("Processing ovpsim log [%s %s %s]: %s" %
("full_trace" if full_trace else "", "stop_on_first_error" if stop else "",
"dont_truncate_after_first_ecall" if dont_truncate_after_first_ecall else
"", ovpsim_log))
# Remove the header part of ovpsim log
cmd = ("sed -i '/Info 1:/,$!d' %s" % ovpsim_log)
os.system(cmd)
# Remove all instructions after ecall (end of program excecution)
cmd = ("sed -i '/ecall/q' %s" % ovpsim_log)
# Remove all instructions after end of trace data (end of program excecution)
if dont_truncate_after_first_ecall:
cmd = ("sed -i '/^Info --/q' %s" % ovpsim_log)
logging.info("Dont truncate logfile after first ecall: %s", ovpsim_log)
else:
cmd = ("sed -i '/ecall/q' %s" % ovpsim_log)
os.system(cmd)
# storage and initial values of gpr and csr
gpr = {}
for g in REGS:
csr = {}
for g in REGS: # base base isa gprs
gpr[g] = 0
for i in range(32): # add in v0-v31 gprs
gpr["v"+str(i)] = 0
csr["vl"] = 0
csr["vtype"] = 0
instr_cnt = 0
with open(ovpsim_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
prev_trace = 0
logit = 0
for line in f:
# Extract instruction infromation
m = re.search(r"riscvOVPsim.*, 0x(?P<addr>.*?)(?P<section>\(.*\): ?)" \
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr>.*?)$", line)
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr_str>.*?)$",
line)
if m:
if prev_trace: # if not yet written as it had no data following it
check_conversion(prev_trace, stop_on_first_error)
# its instruction disassembly line
if prev_trace: # write out the previous one when find next one
check_conversion(prev_trace)
instr_cnt += 1
trace_csv.write_trace_entry(prev_trace)
if verbose2:
logging.debug("prev_trace:: "+str(prev_trace.__dict__))
logging.debug("csr :: "+str(csr))
logging.debug("gpr :: "+str(gpr))
if logit:
# fatal ("stop for now")
pass
prev_trace = 0
trace_bin = m.group("bin")
trace_instr_str = m.group("instr")
trace_addr = m.group("addr")
trace_section = m.group("section")
trace_mode = convert_mode(m.group("mode"), line)
trace_instr = trace_instr_str.split(" ")[0]
instr_cnt += 1
prev_trace = RiscvInstructionTraceEntry()
prev_trace.instr_str = trace_instr_str
prev_trace.binary = trace_bin
prev_trace.addr = trace_addr
prev_trace.privileged_mode = trace_mode
prev_trace.instr = trace_instr
prev_trace.instr_str = m.group("instr_str")
prev_trace.instr = prev_trace.instr_str.split(" ")[0]
prev_trace.binary = m.group("bin")
prev_trace.addr = m.group("addr")
prev_trace.section = m.group("section")
prev_trace.privileged_mode = convert_mode(m.group("mode"), line)
prev_trace.updated_csr = []
prev_trace.updated_gpr = []
if 0:
print ("line ::"+line)
print ("bin ::"+trace_bin)
print ("instr::"+trace_instr_str)
print ("ins ::"+trace_instr)
print ("addr ::"+trace_addr)
print ("sect ::"+trace_section)
print ("mode ::"+trace_mode)
sys.exit(-1)
#if prev_trace.instr in ["vsetvli"]:
#if prev_trace.instr in ["vlh.v"]:
#if prev_trace.instr in ["vmul.vx"]:
if prev_trace.instr in ["vmul.vx_XXX"]:
logit = 1
verbose2 = True
show_line_instr(line, prev_trace)
if full_trace:
i = re.search (r"(?P<instr>[a-z]*?)\s", trace_instr_str)
if i:
trace_instr = i.group("instr")
prev_trace.instr = trace_instr
# TODO - when got full ins decode remove this
if "fsriw" in line or \
"fsw" in line or \
"fsd" in line or \
"fnmsub.d" in line or \
"flw" in line:
logging.debug ("Ignoring ins...(%s) " % (line))
continue
process_if_compressed(prev_trace)
o = re.search (r"(?P<instr>[a-z]*?)\s(?P<operand>.*)", trace_instr_str)
o = re.search (r"(?P<instr>[a-z]*?)\s(?P<operand>.*)",
prev_trace.instr_str)
if o:
operand_str = o.group("operand").replace(" ", "")
operands = operand_str.split(",")
if (prev_trace.instr in ['jalr']):
if (prev_trace.instr in ['jalr', 'c.jalr']):
process_jalr(prev_trace, operands, gpr)
elif (prev_trace.instr in ['jal','c.jal']):
process_jal(prev_trace, operands, gpr)
else:
assign_operand(prev_trace, operands, gpr)
if 'v' in prev_trace.instr[0]:
assign_operand_vector(prev_trace, operands, gpr,
stop_on_first_error)
elif 'f' in prev_trace.instr[0] or "c.f" in prev_trace.instr[0:3]:
pass # ignore floating point. TODO include them
else:
if prev_trace.instr in [
'beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu']:
process_branch_offset (2, operands, prev_trace)
elif prev_trace.instr in [
'c.beqz', 'c.bnez', 'beqz', 'bnez', 'bgez',
'bltz', 'blez', 'bgtz']:
process_branch_offset (1, operands, prev_trace)
assign_operand(prev_trace, operands, gpr,
stop_on_first_error)
else:
# print("no operand for [%s] in [%s]" % (trace_instr, trace_instr_str))
# logging.debug("no operand for [%s] in [%s]" % (trace_instr,
# trace_instr_str))
pass
else:
trace_instr = ""
else:
if 0:
print ("not ins line... [%s]" % (line))
# Extract register value information
n = re.search(r" (?P<rd>[a-z]{1,3}[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
# its a csr, gpr new value or report
if 0: logging.debug ("reg change... [%s]" % (line.strip()))
# Extract register change value information
c = re.search(r" (?P<r>[a-z]*[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$", line)
if n:
# Write the extracted instruction to a csvcol buffer file
# print("%0s %0s = %0s" % (trace_instr_str, m.group("rd"), m.group("val")))
if n.group("rd") != "frm":
rv_instr_trace = RiscvInstructionTraceEntry()
rv_instr_trace.rd = n.group("rd")
rv_instr_trace.rd_val = n.group("val")
rv_instr_trace.rs1 = prev_trace.rs1
rv_instr_trace.rs1_val = prev_trace.rs1_val
rv_instr_trace.rs2 = prev_trace.rs2
rv_instr_trace.rs2_val = prev_trace.rs2_val
rv_instr_trace.instr_str = trace_instr_str
rv_instr_trace.instr = prev_trace.instr
rv_instr_trace.binary = trace_bin
rv_instr_trace.addr = trace_addr
rv_instr_trace.privileged_mode = trace_mode
gpr[rv_instr_trace.rd] = rv_instr_trace.rd_val
check_conversion(rv_instr_trace, stop_on_first_error)
trace_csv.write_trace_entry(rv_instr_trace)
prev_trace = 0 # we wrote out as it had data, so no need to write it next time round
if c and is_csr (c.group("r")):
csr[c.group("r")] = c.group("val")
if verbose2: logging.debug("c:csr %0s = %0s" %
(c.group("r"), c.group("val")))
csr[c.group("r")] = c.group("val")
# prev_trace.updated_csr.append(c.group("r"))
prev_trace.updated_csr.append([c.group("r"), c.group("val")])
continue
n = re.search(r" (?P<r>[a-z]{1,3}[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$", line)
if n: # gpr
if verbose2: logging.debug(("n:gpr %0s = %0s" %
(n.group("r"), n.group("val"))))
if n.group("r") != "frm":
# prev_trace.updated_gpr.append(n.group("r"))
prev_trace.updated_gpr.append([n.group("r"), n.group("val")])
if 'v' in prev_trace.instr[0]:
gpr[n.group("r")] = n.group("val")
else:
# backwards compatible
prev_trace.rd_val = n.group("val")
gpr[prev_trace.rd] = prev_trace.rd_val
if 0:
print ("write entry [[%d]]: rd[%s] val[%s] instr(%s) bin(%s) addr(%s)"
print (
"write entry [[%d]]: rd[%s] val[%s] instr(%s) bin(%s) addr(%s)"
% (instr_cnt, rv_instr_trace.rd, rv_instr_trace.rd_val,
trace_instr_str, trace_bin, trace_addr))
print (rv_instr_trace.__dict__)
sys.exit(-1)
trace_instr_str, prev_trace.binary, prev_trace.addr))
print (rv_instr_trace.__dict__)
sys.exit(-1)
else:
if 0:
print ("write previous entry: [%s] %s " % (str(instr_cnt), line))
sys.exit(-1)
logging.info("Processed instruction count : %d" % instr_cnt)
line = line.strip()
if verbose2: logging.debug("ignoring line: [%s] %s " %
(str(instr_cnt), line))
line = re.sub(' +', ' ', line)
split = line.split(" ")
if len(split) == 1: continue
item = split[1]
if "----" in item: continue
if "REPORT" in line or item in [ # TODO sort csrs
"mtvec","pmpaddr0","pmpcfg0","mstatus","mepc","mscratch",
"mcause","mtval","vl","vtype","sstatus"]:
logging.debug("Ignoring: [%d] [[%s]]" % (instr_cnt, line))
pass
elif "Warning (RISCV_" in line:
logging.debug("Skipping: [%d] (%s) [[%s]]" %
(instr_cnt, prev_trace.instr_str, line))
prev_trace.instr = "nop"
prev_trace.instr_str = "nop"
else:
logging.debug("<unknown> (%s) in line: [%s] %s " %
(item, str(instr_cnt), line))
if stop_on_first_error:
fatal ("")
logging.info("Processed instruction count : %d " % instr_cnt)
if instr_cnt == 0:
logging.error ("No Instructions in logfile: %s" % ovpsim_log)
sys.exit(-1)
logging.info("CSV saved to : %s" % csv)
def main():
""" if used standalone set up for testing """
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input ovpsim simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
parser.add_argument("-f", "--full_trace", dest="full_trace", action="store_true",
parser.add_argument("--full_trace", dest="full_trace",
action="store_true",
help="Generate the full trace")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
parser.add_argument("--verbose", dest="verbose", action="store_true",
help="Verbose logging")
parser.add_argument("--verbose2", dest="verbose2", action="store_true",
help="Verbose logging detail 2")
parser.add_argument("--stop_on_first_error", dest="stop_on_first_error",
action="store_true",
help="Stop on first error")
parser.add_argument("--dont_truncate_after_first_ecall",
dest="dont_truncate_after_first_ecall",
action="store_true",
help="Dont truncate on first ecall")
parser.set_defaults(full_trace=False)
parser.set_defaults(verbose=False)
parser.set_defaults(verbose2=False)
parser.set_defaults(stop_on_first_error=False)
parser.set_defaults(dont_truncate_after_first_ecall=False)
args = parser.parse_args()
setup_logging(args.verbose)
logging.debug("Logging Debug set")
# Process ovpsim log
process_ovpsim_sim_log(args.log, args.csv, args.full_trace)
process_ovpsim_sim_log(args.log, args.csv, args.full_trace,
args.stop_on_first_error, args.dont_truncate_after_first_ecall,
args.verbose2)
if __name__ == "__main__":

View file

@ -19,6 +19,7 @@ Class for RISC-V instruction trace CSV
import csv
import re
import logging
import sys
class RiscvInstructionTraceEntry(object):
"""RISC-V instruction trace entry"""
@ -37,6 +38,20 @@ class RiscvInstructionTraceEntry(object):
self.instr = ""
self.privileged_mode = ""
self.csr = ""
self.vd = ""
self.vd_val = ""
self.vs1 = ""
self.vs1_val = ""
self.vs2 = ""
self.vs2_val = ""
self.vs3 = ""
self.vs3_val = ""
self.vtype_e = ""
self.vtype_m = ""
self.vtype_d = ""
self.vm = ""
self.updated_csr = ""
self.updated_gpr = ""
def get_trace_string(self):
"""Return a short string of the trace entry"""
@ -56,8 +71,11 @@ class RiscvInstructionTraceCsv(object):
def start_new_trace(self):
"""Create a CSV file handle for a new trace"""
fields = ["instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
"imm", "str", "addr", "binary", "csr", "mode"]
fields = [
"instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
"imm", "str", "addr", "binary", "csr", "mode",
"vd", "vd_val", "vs1", "vs1_val","vs2", "vs2_val","vs3", "vs3_val",
"vtype_e", "vtype_m", "vtype_d", "vm", "updated_csr", "updated_gpr"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
@ -78,18 +96,34 @@ class RiscvInstructionTraceCsv(object):
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.gpr[entry.rd] = entry.rd_val
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'rs1' : entry.rs1,
'rs1_val' : entry.rs1_val,
'rs2' : entry.rs2,
'rs2_val' : entry.rs2_val,
'addr' : entry.addr,
'instr' : entry.instr,
'imm' : entry.imm,
'csr' : entry.csr,
'binary' : entry.binary})
self.csv_writer.writerow({'str' : entry.instr_str,
'rd' : entry.rd,
'rd_val' : entry.rd_val,
'rs1' : entry.rs1,
'rs1_val' : entry.rs1_val,
'rs2' : entry.rs2,
'rs2_val' : entry.rs2_val,
'addr' : entry.addr,
'instr' : entry.instr,
'imm' : entry.imm,
'csr' : entry.csr,
'binary' : entry.binary,
'mode' : entry.privileged_mode,
'vd' : entry.vd,
'vd_val' : entry.vd_val,
'vs1' : entry.vs1,
'vs1_val' : entry.vs1_val,
'vs2' : entry.vs2,
'vs2_val' : entry.vs2_val,
'vs3' : entry.vs3,
'vs3_val' : entry.vs3_val,
'vtype_e' : entry.vtype_e,
'vtype_m' : entry.vtype_m,
'vtype_d' : entry.vtype_d,
'vm' : entry.vm,
'updated_csr': entry.updated_csr,
'updated_gpr': entry.updated_gpr,
})
def gpr_to_abi(gpr):
@ -185,7 +219,7 @@ def get_imm_hex_val(imm):
ADDR_RE = re.compile(r"(?P<imm>[\-0-9]+?)\((?P<rs1>.*)\)")
def assign_operand(trace, operands, gpr):
def assign_operand(trace, operands, gpr, stop_on_first_error = 0):
"""Assign the operand value of the instruction trace"""
if trace.instr in ['lb', 'lh', 'lw', 'lbu', 'lhu', 'ld', 'lq', 'lwu', 'ldu',
'c.lw', 'c.ld', 'c.lq', 'c.lwsp', 'c.ldsp', 'c.lqsp']:
@ -213,11 +247,12 @@ def assign_operand(trace, operands, gpr):
trace.rs1_val = gpr[trace.rs1]
else:
logging.info("Unexpected store address %0s", operands[1])
elif trace.instr in ['mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu',
'mulw', 'muld', 'divw', 'divuw', 'divd', 'remw', 'remd', 'remuw',
'remud', 'sll', 'srl', 'sra', 'add', 'sub', 'xor', 'or', 'and',
'slt', 'sltu', 'sllw', 'slld', 'srlw', 'srld', 'sraw', 'srad',
'addw', 'addd', 'subw', 'subd']:
elif trace.instr in [
'mul', 'mulh', 'mulhsu', 'mulhu', 'div', 'divu', 'rem', 'remu',
'mulw', 'muld', 'divw', 'divuw', 'divd', 'remw', 'remd', 'remuw',
'remud', 'sll', 'srl', 'sra', 'add', 'sub', 'xor', 'or', 'and',
'slt', 'sltu', 'sllw', 'slld', 'srlw', 'srld', 'sraw', 'srad',
'addw', 'addd', 'subw', 'subd']:
# R type instruction
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
@ -225,7 +260,8 @@ def assign_operand(trace, operands, gpr):
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = operands[2]
trace.rs2_val = gpr[trace.rs2]
elif trace.instr in ['c.add', 'c.addw', 'c.mv', 'c.sub', 'c.and', 'c.or', 'c.xor', 'c.subw']:
elif trace.instr in [
'c.add', 'c.addw', 'c.mv', 'c.sub', 'c.and', 'c.or', 'c.xor', 'c.subw']:
# CR type
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
@ -245,9 +281,10 @@ def assign_operand(trace, operands, gpr):
trace.rs1_val = gpr[trace.rs1]
trace.rs2 = 'zero'
trace.rs2_val = '0'
elif trace.instr in ['slli', 'srli', 'srai', 'addi', 'xori', 'ori', 'andi', 'slti',
'sltiu', 'slliw', 'sllid', 'srliw', 'srlid', 'sraiw', 'sraid',
'addiw', 'addid']:
elif trace.instr in [
'slli', 'srli', 'srai', 'addi', 'xori', 'ori', 'andi', 'slti',
'sltiu', 'slliw', 'sllid', 'srliw', 'srlid', 'sraiw', 'sraid',
'addiw', 'addid']:
# I type instruction
trace.rd = operands[0]
trace.rd_val = gpr[trace.rd]
@ -291,8 +328,9 @@ def assign_operand(trace, operands, gpr):
trace.rd_val = gpr[trace.rd]
trace.csr = operands[1]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['scall', 'sbreak', 'fence', 'fence.i', 'ecall', 'ebreak', 'wfi',
'sfence.vma', 'c.ebreak', 'nop', 'c.nop']:
elif trace.instr in [
'scall', 'sbreak', 'fence', 'fence.i', 'ecall', 'ebreak', 'wfi',
'sfence.vma', 'c.ebreak', 'nop', 'c.nop']:
trace.rd = 'zero'
trace.rs1 = 'zero'
trace.rs2 = 'zero'
@ -318,8 +356,13 @@ def assign_operand(trace, operands, gpr):
trace.rs1 = operands[1]
trace.rs1_val = gpr[trace.rs1]
trace.imm = get_imm_hex_val(operands[2])
elif trace.instr in ['c.j', 'c.jal']:
elif trace.instr in ['c.j']:
trace.imm = get_imm_hex_val(operands[0])
elif trace.instr in ['c.jal']:
if len(operands) == 1:
trace.imm = get_imm_hex_val(operands[0])
else:
trace.imm = get_imm_hex_val(operands[1])
# Pseudo instruction convertion below
elif trace.instr in ['mv']:
trace.instr = 'addi'
@ -449,5 +492,7 @@ def assign_operand(trace, operands, gpr):
pass
else:
# TODO: Support other instructions
logging.info("Unsupported instr : %s" % trace.instr)
logging.debug("Unsupported instr : %s (%s)" %
(trace.instr, trace.instr_str))
if stop_on_first_error:
sys.exit(-1)

View file

@ -0,0 +1,93 @@
"""
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.
Convert whisper sim log to standard riscv instruction trace format
"""
import argparse
import os
import re
import sys
import logging
sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from riscv_trace_csv import *
from lib import *
INSTR_RE = re.compile(r"#(?P<n>[0-9]+?)\s+(?P<mode>[0-9]+?)\s+(?P<pc>[0-9a-f]+?)\s+" \
"(?P<bin>[0-9a-f]+?)\s+(?P<type>[a-z]+?)\s+" \
"(?P<reg>[0-9a-f]+?)\s+(?P<val>[0-9a-f]+?)\s+(?P<instr>.*?)$")
LOGGER = logging.getLogger()
def process_whisper_sim_log(whisper_log, csv, full_trace = 0):
"""Process SPIKE simulation log.
Extract instruction and affected register information from whisper simulation
log and save to a list.
"""
logging.info("Processing whisper log : %s" % whisper_log)
instr_cnt = 0
whisper_instr = ""
gpr = {}
gpr["zero"] = 0
with open(whisper_log, "r") as f, open(csv, "w") as csv_fd:
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
for line in f:
# Extract instruction infromation
m = INSTR_RE.search(line)
if m:
logging.debug("-> mode: %s, pc:%s, bin:%s, instr:%s" %
(m.group('mode'), m.group('pc'), m.group('bin'), m.group('instr')))
if re.search('ecall', m.group('instr')):
break
if m.group('type') == 'r':
whisper_instr = m.group("instr").replace("\. + ", "")
whisper_instr = whisper_instr.replace("\. - ", "-")
rv_instr_trace = RiscvInstructionTraceEntry()
rv_instr_trace.instr_str = whisper_instr
rv_instr_trace.binary = m.group("bin")
reg = "x" + str(int(m.group("reg"), 16))
rv_instr_trace.rd = gpr_to_abi(reg)
rv_instr_trace.rd_val = m.group("val")
trace_csv.write_trace_entry(rv_instr_trace)
instr_cnt += 1
logging.info("Processed instruction count : %d" % instr_cnt)
logging.info("CSV saved to : %s" % csv)
def main():
instr_trace = []
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input whisper simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
parser.add_argument("-f", "--full_trace", dest="full_trace", action="store_true",
help="Generate the full trace")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
help="Verbose logging")
parser.set_defaults(full_trace=False)
parser.set_defaults(verbose=False)
args = parser.parse_args()
setup_logging(args.verbose)
# Process whisper log
process_whisper_sim_log(args.log, args.csv, args.full_trace)
if __name__ == "__main__":
main()

View file

@ -340,7 +340,7 @@ class riscv_asm_program_gen extends uvm_object;
init_floating_point_gpr();
end
core_is_initialized();
gen_dummy_csr_write();
gen_dummy_csr_write(); // comment out if not want to read incorrect values from csr
endfunction
// Setup MISA based on supported extensions

View file

@ -857,14 +857,12 @@ class riscv_instr_cover_group;
// instr_trans_cg = new();
branch_hit_history_cg = new();
rv32i_misc_cg = new();
if (!cfg.disable_compressed_instr) begin
illegal_compressed_instr_cg = new();
end
opcode_cg = new();
if (RV32C inside {supported_isa}) begin
illegal_compressed_instr_cg = new();
compressed_opcode_cg = new();
hint_cg = new();
end
opcode_cg = new();
if (RV32M inside {supported_isa}) begin
mul_cg = new();
mulh_cg = new();
@ -940,7 +938,7 @@ class riscv_instr_cover_group;
privileged_csr_cg = new();
mcause_exception_cg = new();
mcause_interrupt_cg = new();
if (!cfg.disable_compressed_instr) begin
if (RV32C inside {supported_isa}) begin
mepc_alignment_cg = new();
end
mstatus_m_cg = new();
@ -1075,7 +1073,7 @@ class riscv_instr_cover_group;
C_ADDW : c_addw_cg.sample(instr);
C_ADDIW : c_addiw_cg.sample(instr);
default: begin
if (!cfg.disable_compressed_instr) begin
if (RV32C inside {supported_isa}) begin
illegal_compressed_instr_cg.sample(instr.binary);
end
if (instr.group == RV32I) begin
@ -1107,7 +1105,7 @@ class riscv_instr_cover_group;
end
end
MEPC: begin
if (!cfg.disable_compressed_instr) begin
if (RV32C inside {supported_isa}) begin
mepc_alignment_cg.sample(instr.rd_value);
end
end

View file

@ -36,6 +36,8 @@ class riscv_instr_cov_test extends uvm_test;
i++;
end
cfg = riscv_instr_gen_config::type_id::create("cfg");
// disable_compressed_instr is not relevant to coverage test
cfg.disable_compressed_instr = 0;
cfg.build_instruction_template(.skip_instr_exclusion(1));
instr = riscv_instr_cov_item::type_id::create("instr");
instr.rand_mode(0);

View file

@ -32,3 +32,8 @@
path_var: SAIL_RISCV
cmd: >
<path_var> <elf>
- iss: whisper
path_var: WHISPER_ISS
cmd: >
<path_var> <elf> --log --xlen <xlen> --isa <variant>