Update google_riscv-dv to google/riscv-dv@39797b2

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

* coverage flow updates (Udi Jonnalagadda)
* Update src/riscv_debug_rom_gen.sv (Tom Roberts)
* debug_rom_gen: Fix return address issue (Tom Roberts)
* Add sfence.vma after PTE process (google/riscv-dv#731) (taoliug)
* generate gen_config data in tabular format (aneels3)
* Fix coverage issue for ml target (google/riscv-dv#729) (taoliug)
* Fix index offset constraint conflict (google/riscv-dv#728) (taoliug)
* Fix rcs import and create_instr function (aneels3)
* Fix setup_misa and formatting issue (aneels3)
* Fix SPIKE ISSUE google/riscv-dv#722 (aneels3)
* Fix coverage issue (aneels3)
* fix google/riscv-dv#725 (Udi Jonnalagadda)
* Fix formatting and linting issue (aneels3)
* Add function setup_misa (ShraddhaDevaiya)
* Fix gen_trap_handler_section (aneels3)
* Add constraint (ShraddhaDevaiya)
* Fixed push_gpr_to_kernel def (Saurabh Singh)
* Add ic file to the target dir (aneels3)
* Fix timeout issue (aneels3)
* add mtvec constraint (pvipsyash)
* Fix create_instr issue (aneels3)
* Add function push_gpr_to_kernel (ishitapvips)
* Fix invalid CSR test for RV64GCV target (google/riscv-dv#720)
  (taoliug)
* Fix solve...before... on non-rand variables issue (google/riscv-
  dv#719) (taoliug)
* Add rv32c instructions (aneels3)
* Modify riscv_instr class fields (aneels3)
* Fix import issue (aneels3)
* Significantly improves performance of pyflow functional coverage
  (through changing the way that covergroups are instantiated & data
  are sampled) (Hodjat Asghari Esfeden)
* fix jumps to `test_done` and `init_[m/s/u]_mode` (google/riscv-
  dv#710) (udinator)
* Fix multi-harts program generation with PMP enabled (google/riscv-
  dv#716) (taoliug)
* Fix google/riscv-dv#681 (google/riscv-dv#715) (taoliug)
* Add initial support for rv32imc (aneels3)
* resolve conflicts (aneels3)
* add rv32imc core setting (pvipsyash)
* changes for core settings (pvipsyash)
* add riscv_compressed_instr (aneels3)
* Convert code to be PEP8 compliant (Hodjat Asghari Esfeden)
* Add riscv_data_page_gen (aneels3)
* Integrates functional coverage side of pyflow into cov.py
  (google/riscv-dv#708) (Hodjat Asghari Esfeden)
* Workaround of the SV compilation problem caused by assigning the
  const array variable with the empty concatenation. (google/riscv-
  dv#704) (Dariusz Stachańczyk)
* Add rv32m and rv32c instr defines (ShraddhaDevaiya)
* Fix logging issue along with other minor fixes (Hodjat Asghari
  Esfeden)
* Add push_stack and pop_stack instr. (ShraddhaDevaiya)
* Fix minor issues (aneels3)
* Add a target for RV32IMC with SV32 address translation
  (google/riscv-dv#699) (taoliug)
* Fixes a minot import issue (Hodjat Asghari Esfeden)
* Fix LR/SC sequence issue (google/riscv-dv#698) (taoliug)
* fix ebreak generation bug (google/riscv-dv#689) (udinator)
* Update vector extension to v0.9 (google/riscv-dv#697) (taoliug)
* Fixes a few issues in riscv_asm_program_gen and
  riscv_instr_gen_config (Hodjat Asghari Esfeden)
* fix iterate over args dict (pvipsyash)
* fix parse_args (pvipsyash)
* fix cmdline argparse for directed stream (pvipsyash)
* [pygen/riscv_instr_stream] Fix ebreak generation (Udi Jonnalagadda)
* Fix flake8 related formatting (aneels3)
* Add jal instr (aneels3)
* Fixes for same rd for main instructions (Saurabh Singh)
* Fixes to resolve label issue for directed class (Saurabh Singh)
* Add constraint on jump_start (ShraddhaDevaiya)
* Add Constraint for jump instructions. (ShraddhaDevaiya)
* fix minor issue in directed_lib (pvipsyash)
* Add riscv_jal_instr to directed_lib (pvipsyash)
* Fix forward branch label compilation error (aneels3)

Signed-off-by: Udi Jonnalagadda <udij@google.com>
This commit is contained in:
Udi Jonnalagadda 2020-10-23 15:41:26 -07:00 committed by udinator
parent 5d4c9aebe2
commit 9b656a0a2c
63 changed files with 5509 additions and 3731 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: 2e5251846efb5fa42882a2b6b571ef8693e8cd60
rev: 39797b2f07784e775149a4f05c90fee2427124e5
}
}

View file

@ -30,279 +30,315 @@ from types import SimpleNamespace
LOGGER = logging.getLogger()
def build_cov(out, cfg, cwd, opts_vec, opts_cov):
"""Building the coverage collection framework
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
opts_vec : Vector options
opts_cov : Coverage options
"""
# Convert key dictionary to argv variable
argv= SimpleNamespace(**cfg)
logging.info("Building the coverage collection framework")
build_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s "
" --co -o %s --cov -tl %s %s --cmp_opts \"%s %s\" --noclean" %
(cwd, argv.simulator, argv.simulator_yaml, out, argv.testlist, argv.opts,
opts_vec, opts_cov))
if argv.target:
build_cmd += (" --target %s" % argv.target)
if argv.custom_target:
build_cmd += (" --custom_target %s" % argv.custom_target)
if argv.stop_on_first_error:
build_cmd += (" --stop_on_first_error")
if argv.lsf_cmd != "":
build_cmd += (" --lsf_cmd \"%s\"" % argv.lsf_cmd)
run_parallel_cmd([build_cmd], argv.timeout, debug_cmd = argv.debug)
else:
run_cmd(build_cmd, debug_cmd = argv.debug)
def build_cov(out, cfg, cwd, opts_vec, opts_cov):
"""Building the coverage collection framework
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
opts_vec : Vector options
opts_cov : Coverage options
"""
# Convert key dictionary to argv variable
argv = SimpleNamespace(**cfg)
logging.info("Building the coverage collection framework")
build_cmd = ("python3 {}/run.py --simulator {} --simulator_yaml {} "
" --co -o {} --cov -tl {} {} --cmp_opts \"{} {}\" --noclean".format(
cwd, argv.simulator, argv.simulator_yaml, out,
argv.testlist, argv.opts, opts_vec, opts_cov))
if argv.target:
build_cmd += (" --target {}".format(argv.target))
if argv.custom_target:
build_cmd += (" --custom_target {}".format(argv.custom_target))
if argv.stop_on_first_error:
build_cmd += " --stop_on_first_error"
if argv.lsf_cmd != "":
build_cmd += (" --lsf_cmd \"{}\"".format(argv.lsf_cmd))
run_parallel_cmd([build_cmd], argv.timeout, debug_cmd=argv.debug)
else:
run_cmd(build_cmd, debug_cmd=argv.debug)
def sim_cov(out, cfg, cwd, opts_vec, opts_cov, csv_list):
"""Simulation the coverage collection
"""Simulation the coverage collection
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
opts_vec : Vector options
opts_cov : Coverage options
csv_list : The list of trace csv
"""
# Convert key dictionary to argv variable
argv= SimpleNamespace(**cfg)
logging.info("Collecting functional coverage from %0d trace CSV" % len(csv_list))
test_name = "riscv_instr_cov_test"
base_sim_cmd = ("python3 %s/run.py --simulator %s --simulator_yaml %s --noclean "
"--so -o %s --cov -tl %s %s "
"-tn %s --steps gen --sim_opts \"<trace_csv_opts> %s %s\" " %
(cwd, argv.simulator, argv.simulator_yaml, out, argv.testlist,
argv.opts, test_name, opts_vec, opts_cov))
if argv.target:
base_sim_cmd += (" --target %s" % argv.target)
if argv.custom_target:
base_sim_cmd += (" --custom_target %s" % argv.custom_target)
if argv.stop_on_first_error:
base_sim_cmd += (" --stop_on_first_error")
file_idx = 0
trace_idx = 0
trace_csv_opts = ""
batch_cnt = 1
sim_cmd_list = []
if argv.batch_size > 0:
batch_cnt = (len(csv_list) + argv.batch_size - 1)/ argv.batch_size;
logging.info("Batch size: %0d, Batch cnt:%0d" % (argv.batch_size, batch_cnt))
for i in range(len(csv_list)):
file_idx = 0
trace_idx = i
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
opts_vec : Vector options
opts_cov : Coverage options
csv_list : The list of trace csv
"""
# Convert key dictionary to argv variable
argv = SimpleNamespace(**cfg)
logging.info(
"Collecting functional coverage from {} trace CSV".format(len(csv_list)))
test_name = "riscv_instr_cov_test"
base_sim_cmd = (
"python3 {}/run.py --simulator {} --simulator_yaml {} --noclean "
"--so -o {} --cov -tl {} {} "
"-tn {} --steps gen --sim_opts \"<trace_csv_opts> {} {} <visualization>\" "
.format(cwd, argv.simulator, argv.simulator_yaml, out,
argv.testlist,
argv.opts, test_name, opts_vec, opts_cov))
if argv.simulator == "pyflow" and argv.enable_visualization:
base_sim_cmd = re.sub("<visualization>", "--enable_visualization",
base_sim_cmd)
else:
base_sim_cmd = re.sub("<visualization>", "", base_sim_cmd)
if argv.target:
base_sim_cmd += (" --target {}".format(argv.target))
if argv.custom_target:
base_sim_cmd += (" --custom_target {}".format(argv.custom_target))
if argv.stop_on_first_error:
base_sim_cmd += " --stop_on_first_error"
trace_csv_opts = ""
batch_cnt = 1
sim_cmd_list = []
if argv.batch_size > 0:
file_idx = i / argv.batch_size;
trace_idx = i % argv.batch_size;
trace_csv_opts += (" +trace_csv_%0d=%s" % (trace_idx, csv_list[i]))
if ((i == len(csv_list)-1) or ((argv.batch_size > 0) and (trace_idx == argv.batch_size-1))):
sim_cmd = base_sim_cmd.replace("<trace_csv_opts>", trace_csv_opts)
sim_cmd += (" --log_suffix _%d" % file_idx)
if argv.lsf_cmd == "":
logging.info("Processing batch %0d/%0d" % (file_idx+1, batch_cnt))
run_cmd(sim_cmd, debug_cmd = argv.debug)
else:
sim_cmd += (" --lsf_cmd \"%s\"" % argv.lsf_cmd)
sim_cmd_list.append(sim_cmd)
trace_csv_opts = ""
if argv.lsf_cmd != "":
run_parallel_cmd(sim_cmd_list, argv.timeout)
logging.info("Collecting functional coverage from %0d trace CSV...done" % len(csv_list))
batch_cnt = (len(csv_list) + argv.batch_size - 1) / argv.batch_size
logging.info(
"Batch size: {}, Batch cnt: {}".format(argv.batch_size, batch_cnt))
for i in range(len(csv_list)):
file_idx = 0
trace_idx = i
if argv.batch_size > 0:
file_idx = i / argv.batch_size
trace_idx = i % argv.batch_size
if argv.simulator == "pyflow":
if i == 0:
trace_csv_opts += (" --trace_csv={}".format(csv_list[i]))
else:
trace_csv_opts += (",{}".format(csv_list[i]))
else:
trace_csv_opts += (" +trace_csv_{}={}".format(trace_idx, csv_list[i]))
if (i == len(csv_list) - 1) or (
(argv.batch_size > 0) and (trace_idx == argv.batch_size - 1)):
sim_cmd = base_sim_cmd.replace("<trace_csv_opts>", trace_csv_opts)
sim_cmd += (" --log_suffix _{}".format(file_idx))
if argv.lsf_cmd == "":
logging.info(
"Processing batch {}/{}".format(file_idx + 1, batch_cnt))
run_cmd(sim_cmd, debug_cmd=argv.debug)
else:
sim_cmd += (" --lsf_cmd \"{}\"".format(argv.lsf_cmd))
sim_cmd_list.append(sim_cmd)
trace_csv_opts = ""
if argv.lsf_cmd != "":
run_parallel_cmd(sim_cmd_list, argv.timeout)
logging.info(
"Collecting functional coverage from {} trace CSV...done".format(len(
csv_list)))
def collect_cov(out, cfg, cwd):
"""Collect functional coverage from the instruction trace
"""Collect functional coverage from the instruction trace
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
"""
# Convert key dictionary to argv variable
argv= SimpleNamespace(**cfg)
log_list = []
csv_list = []
if not argv.dir:
logging.error("Missing directory of trace log files")
sys.exit(RET_FAIL)
logging.info("Processing trace log under %s" % argv.dir)
if not os.path.isdir(argv.dir) or not os.listdir(argv.dir):
if not argv.debug:
logging.error("Cannot find %s directory, or it is empty", argv.dir)
sys.exit(RET_FAIL)
if argv.core:
Args:
out : Output directory
cfg : Loaded configuration dictionary.
cwd : Filesystem path to RISCV-DV repo
"""
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, argv.core))
run_cmd("find %s -name \"*.csv\" | sort > %s" % (argv.dir, trace_log))
else:
trace_log = ("%s/%s_trace_log" % (out, argv.iss))
run_cmd("find %s -name \"*.log\" | sort > %s" % (argv.dir, trace_log))
with open(trace_log) as f:
for line in f:
line = line.rstrip()
log_list.append(line)
csv = line[0:-4] + ".csv"
csv_list.append(csv)
if argv.steps == "all" or re.match("csv", argv.steps):
for i in range(len(log_list)):
log = log_list[i]
csv = csv_list[i]
# If a core target is defined, prioritize over ISS
if argv.core:
logging.info("Process %0s log[%0d/%0d] : %s" % (argv.core, i+1, len(log_list), log))
else:
logging.info("Process %0s log[%0d/%0d] : %s" % (argv.iss, i+1, len(log_list), log))
if argv.iss == "spike":
process_spike_sim_log(log, csv, 1)
elif argv.iss == "ovpsim":
process_ovpsim_sim_log(log, csv, argv.stop_on_first_error,
argv.dont_truncate_after_first_ecall, 1)
else:
logging.error("Full trace for %s is not supported yet" % argv.iss)
sys.exit(RET_FAIL)
if argv.steps == "all" or re.match("cov", argv.steps):
opts_vec = ""
opts_cov = ""
if argv.vector_options:
opts_vec = ("%0s" % argv.vector_options)
if argv.coverage_options:
opts_cov = ("%0s" % argv.coverage_options)
if argv.compliance_mode:
opts_cov += " +define+COMPLIANCE_MODE"
# Building the coverage collection framework
build_cov(out, cfg, cwd, opts_vec, opts_cov)
# Simulation the coverage collection
sim_cov(out, cfg, cwd, opts_vec, opts_cov, csv_list)
# Convert key dictionary to argv variable
argv = SimpleNamespace(**cfg)
log_list = []
csv_list = []
if not argv.dir:
logging.error("Missing directory of trace log files")
sys.exit(RET_FAIL)
logging.info("Processing trace log under {}".format(argv.dir))
if not os.path.isdir(argv.dir) or not os.listdir(argv.dir):
if not argv.debug:
logging.error("Cannot find {} directory, or it is empty".format(argv.dir))
sys.exit(RET_FAIL)
if argv.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 = ("{}/{}_trace_log".format(out, argv.core))
run_cmd("find {} -name \"*.csv\" | sort > {}".format(argv.dir, trace_log))
else:
trace_log = ("{}/{}_trace_log".format(out, argv.iss))
run_cmd("find {} -name \"*.log\" | sort > {}".format(argv.dir, trace_log))
with open(trace_log) as f:
for line in f:
line = line.rstrip()
log_list.append(line)
csv = line[0:-4] + ".csv"
csv_list.append(csv)
if argv.steps == "all" or re.match("csv", argv.steps):
for i in range(len(log_list)):
log = log_list[i]
csv = csv_list[i]
# If a core target is defined, prioritize over ISS
if argv.core:
logging.info("Process {} log[{}/{}] : {}".format(
argv.core, i + 1, len(log_list), log))
else:
logging.info("Process {} log[{}/{}] : {}".format(
argv.iss, i + 1, len(log_list), log))
if argv.iss == "spike":
process_spike_sim_log(log, csv, 1)
elif argv.iss == "ovpsim":
process_ovpsim_sim_log(log, csv, argv.stop_on_first_error,
argv.dont_truncate_after_first_ecall,
1)
else:
logging.error(
"Full trace for {} is not supported yet".format(argv.iss))
sys.exit(RET_FAIL)
if argv.steps == "all" or re.match("cov", argv.steps):
opts_vec = ""
opts_cov = ""
if argv.vector_options:
opts_vec = ("{}".format(argv.vector_options))
if argv.coverage_options:
opts_cov = ("{}".format(argv.coverage_options))
if argv.compliance_mode:
opts_cov += " +define+COMPLIANCE_MODE"
# Building the coverage collection framework
if argv.simulator != "pyflow":
build_cov(out, cfg, cwd, opts_vec, opts_cov)
# Simulation the coverage collection
sim_cov(out, cfg, cwd, opts_vec, opts_cov, csv_list)
def setup_parser():
"""Create a command line parser.
"""Create a command line parser.
Returns: The created parser.
"""
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("-o", "--output", type=str,
help="Output directory name", dest="o")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true", default=False,
help="Verbose logging")
parser.add_argument("--dir", type=str,
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("--compliance_mode", action="store_true", default=False,
help="Run the coverage model in compliance test mode")
parser.add_argument("-i", "--instr_cnt", dest="instr_cnt", type=int, default=0,
help="Random instruction count for debug mode")
parser.add_argument("-to", "--timeout", dest="timeout", type=int, default=1000,
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="",
help="Regression testlist", dest="testlist")
parser.add_argument("--opts", type=str, default="",
help="Additional options for the instruction generator")
parser.add_argument("--lsf_cmd", type=str, default="",
help="LSF command. Run in local sequentially if lsf \
Returns: The created parser.
"""
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("-o", "--output", type=str,
help="Output directory name", dest="o")
parser.add_argument("-v", "--verbose", dest="verbose", action="store_true",
default=False,
help="Verbose logging")
parser.add_argument("--dir", type=str,
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("--compliance_mode", action="store_true", default=False,
help="Run the coverage model in compliance test mode")
parser.add_argument("-i", "--instr_cnt", dest="instr_cnt", type=int,
default=0,
help="Random instruction count for debug mode")
parser.add_argument("-to", "--timeout", dest="timeout", type=int,
default=1000,
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="",
help="Regression testlist", dest="testlist")
parser.add_argument("--opts", type=str, default="",
help="Additional options for the instruction generator")
parser.add_argument("--lsf_cmd", type=str, default="",
help="LSF command. Run in local sequentially if lsf \
command is not specified")
parser.add_argument("--target", type=str, default="rv32imc",
help="Run the generator with pre-defined targets: \
parser.add_argument("--target", type=str, default="rv32imc",
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, \
parser.add_argument("-si", "--simulator", type=str, default="vcs",
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", default=False,
help="Stop on detecting first error")
parser.add_argument("--dont_truncate_after_first_ecall", dest="dont_truncate_after_first_ecall",
action="store_true", default=False,
help="Do not truncate log and csv file on first ecall")
parser.add_argument("--noclean", action="store_true", default=False,
help="Do not clean the output of the previous runs")
parser.add_argument("--vector_options", type=str, default="",
help="Enable Vectors and set options")
parser.add_argument("--coverage_options", type=str, default="",
help="Controlling coverage coverpoints")
parser.add_argument("--exp", action="store_true", default=False,
help="Run generator with experimental features")
parser.add_argument("-d", "--debug", type=str, default="",
help="Generate debug command log file")
return parser
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", default=False,
help="Stop on detecting first error")
parser.add_argument("--dont_truncate_after_first_ecall",
dest="dont_truncate_after_first_ecall",
action="store_true", default=False,
help="Do not truncate log and csv file on first ecall")
parser.add_argument("--noclean", action="store_true", default=False,
help="Do not clean the output of the previous runs")
parser.add_argument("--vector_options", type=str, default="",
help="Enable Vectors and set options")
parser.add_argument("--coverage_options", type=str, default="",
help="Controlling coverage coverpoints")
parser.add_argument("--exp", action="store_true", default=False,
help="Run generator with experimental features")
parser.add_argument("-d", "--debug", type=str, default="",
help="Generate debug command log file")
parser.add_argument("--enable_visualization", action="store_true",
default=False,
help="Enabling coverage report visualization for pyflow")
return parser
def load_config(args, cwd):
"""
Load configuration from the command line and the configuration file.
"""
Load configuration from the command line and the configuration file.
Args:
args: Parsed command-line configuration
Returns:
Loaded configuration dictionary.
"""
if args.debug:
args.debug = open(args.debug, "w")
if args.verbose:
args.opts += "-v"
Args:
args: Parsed command-line configuration
Returns:
Loaded configuration dictionary.
"""
if args.debug:
args.debug = open(args.debug, "w")
if args.verbose:
args.opts += "-v"
if not args.simulator_yaml:
args.simulator_yaml = cwd + "/yaml/simulator.yaml"
if not args.simulator_yaml:
args.simulator_yaml = cwd + "/yaml/simulator.yaml"
# Disable ISS coverage if a core is passed in
if args.core:
args.iss = ""
# 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:
if not args.custom_target:
args.custom_target = args.core_setting_dir
else:
args.core_setting_dir = args.custom_target
# Keep the core_setting_dir option to be backward compatible, suggest to use
# --custom_target
if args.core_setting_dir:
if not args.custom_target:
args.custom_target = args.core_setting_dir
else:
args.core_setting_dir = args.custom_target
args.core_setting_dir = cwd + "/target/" + args.target
if not args.custom_target:
args.core_setting_dir = cwd + "/target/"+ args.target
args.testlist = cwd + "/yaml/cov_testlist.yaml" ## needed if need to force
# Create loaded configuration dictionary.
cfg = vars(args)
return cfg
args.testlist = cwd + "/yaml/cov_testlist.yaml" ## needed if need to force
# Create loaded configuration dictionary.
cfg = vars(args)
return cfg
def main():
"""This is the main entry point."""
try:
parser = setup_parser()
args = parser.parse_args()
cwd = os.path.dirname(os.path.realpath(__file__))
setup_logging(args.verbose)
# Load configuration from the command line and the configuration file.
cfg = load_config(args, cwd)
# Create output directory
output_dir = create_output(args.o, args.noclean, "cov_out_")
# Collect coverage for the trace CSV
collect_cov(output_dir, cfg ,cwd)
logging.info("Coverage results are saved to %s" % output_dir)
sys.exit(RET_SUCCESS)
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
"""This is the main entry point."""
try:
parser = setup_parser()
args = parser.parse_args()
cwd = os.path.dirname(os.path.realpath(__file__))
setup_logging(args.verbose)
# Load configuration from the command line and the configuration file.
cfg = load_config(args, cwd)
# Create output directory
output_dir = create_output(args.o, args.noclean, "cov_out_")
# Collect coverage for the trace CSV
collect_cov(output_dir, cfg, cwd)
logging.info("Coverage results are saved to {}".format(output_dir))
sys.exit(RET_SUCCESS)
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
if __name__ == "__main__":
main()
main()

View file

@ -0,0 +1,228 @@
"""
Copyright 2020 Google LLC
Copyright 2020 PerfectVIPs Inc.
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.
"""
import logging
import vsc
from pygen_src.isa.riscv_instr import riscv_instr
from pygen_src.riscv_instr_pkg import (riscv_instr_name_t, riscv_instr_format_t,
riscv_instr_category_t, riscv_reg_t, imm_t, pkg_ins)
@vsc.randobj
class riscv_compressed_instr(riscv_instr):
def __init__(self):
super().__init__()
self.imm_align = 0
self.rs1 = riscv_reg_t.S0
self.rs2 = riscv_reg_t.S0
self.rd = riscv_reg_t.S0
self.is_compressed = 1
@vsc.constraint
def rvc_csr_c(self):
# Registers specified by the three-bit rs1, rs2, and rd
with vsc.implies(self.format.inside(vsc.rangelist(riscv_instr_format_t.CIW_FORMAT,
riscv_instr_format_t.CL_FORMAT,
riscv_instr_format_t.CS_FORMAT,
riscv_instr_format_t.CB_FORMAT,
riscv_instr_format_t.CA_FORMAT))):
with vsc.implies(self.has_rs1 == 1):
self.rs1.inside(vsc.rangelist(riscv_reg_t.S0, riscv_reg_t.S1, riscv_reg_t.A0,
riscv_reg_t.A1, riscv_reg_t.A2, riscv_reg_t.A3,
riscv_reg_t.A4, riscv_reg_t.A5))
with vsc.implies(self.has_rs2 == 1):
self.rs2.inside(vsc.rangelist(riscv_reg_t.S0, riscv_reg_t.S1, riscv_reg_t.A0,
riscv_reg_t.A1, riscv_reg_t.A2, riscv_reg_t.A3,
riscv_reg_t.A4, riscv_reg_t.A5))
with vsc.implies(self.has_rd == 1):
self.rd.inside(vsc.rangelist(riscv_reg_t.S0, riscv_reg_t.S1, riscv_reg_t.A0,
riscv_reg_t.A1, riscv_reg_t.A2, riscv_reg_t.A3,
riscv_reg_t.A4, riscv_reg_t.A5))
# _ADDI16SP is only valid when rd == SP
with vsc.implies(self.instr_name == riscv_instr_name_t.C_ADDI16SP):
self.rd == riscv_reg_t.SP
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.C_JR,
riscv_instr_name_t.C_JALR))):
self.rs1 != riscv_reg_t.ZERO
self.rs2 == riscv_reg_t.ZERO
@vsc.constraint
def imm_val_c(self):
with vsc.implies(self.imm_type.inside(vsc.rangelist(imm_t.NZIMM, imm_t.NZUIMM))):
self.imm[5:0] != 0
with vsc.implies(self.instr_name == riscv_instr_name_t.C_LUI):
self.imm[31:5] == 0
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.C_SRAI,
riscv_instr_name_t.C_SRLI,
riscv_instr_name_t.C_SLLI))):
self.imm[31:5] == 0
with vsc.implies(self.instr_name == riscv_instr_name_t.C_ADDI4SPN):
self.imm[1:0] == 0
# C_JAL is RV32C only instruction
@vsc.constraint
def jal_c(self):
with vsc.implies(self.XLEN != 32):
self.instr_name != riscv_instr_name_t.C_JAL
# Avoid generating HINT or illegal instruction by default as it's not supported by the compiler
@vsc.constraint
def no_hint_illegal_instr_c(self):
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.C_ADDI,
riscv_instr_name_t.C_ADDIW,
riscv_instr_name_t.C_LI,
riscv_instr_name_t.C_LUI,
riscv_instr_name_t.C_SLLI,
riscv_instr_name_t.C_SLLI64,
riscv_instr_name_t.C_LQSP,
riscv_instr_name_t.C_LDSP,
riscv_instr_name_t.C_MV,
riscv_instr_name_t.C_ADD,
riscv_instr_name_t.C_LWSP))):
self.rd != riscv_reg_t.ZERO
with vsc.implies(self.instr_name == riscv_instr_name_t.C_JR):
self.rs1 != riscv_reg_t.ZERO
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.C_ADD,
riscv_instr_name_t.C_MV))):
self.rs2 != riscv_reg_t.ZERO
with vsc.implies(self.instr_name == riscv_instr_name_t.C_LUI):
self.rd != riscv_reg_t.SP
def set_imm_len(self):
if self.format in [riscv_instr_format_t.CI_FORMAT, riscv_instr_format_t.CSS_FORMAT]:
self.imm_len = 6
elif self.format in [riscv_instr_format_t.CL_FORMAT, riscv_instr_format_t.CS_FORMAT]:
self.imm_len = 5
elif self.format in [riscv_instr_format_t.CJ_FORMAT]:
self.imm_len = 11
elif self.format in [riscv_instr_format_t.CB_FORMAT]:
if self.instr_name == riscv_instr_name_t.C_ANDI:
self.imm_len = 6
else:
self.imm_len = 7
elif self.format in [riscv_instr_format_t.CB_FORMAT, riscv_instr_format_t.CIW_FORMAT]:
self.imm_len = 8
if self.instr_name in [riscv_instr_name_t.C_SQ, riscv_instr_name_t.C_LQ,
riscv_instr_name_t.C_LQSP, riscv_instr_name_t.C_SQSP,
riscv_instr_name_t.C_ADDI16SP]:
self.imm_align = 4
elif self.instr_name in [riscv_instr_name_t.C_SD, riscv_instr_name_t.C_LD,
riscv_instr_name_t.C_LDSP, riscv_instr_name_t.C_SDSP]:
self.imm_align = 3
elif self.instr_name in [riscv_instr_name_t.C_SW, riscv_instr_name_t.C_LW,
riscv_instr_name_t.C_LWSP, riscv_instr_name_t.C_SWSP,
riscv_instr_name_t.C_ADDI4SPN]:
self.imm_align = 2
elif self.instr_name in [riscv_instr_name_t.C_LUI]:
self.imm_align = 12
elif self.instr_name in [riscv_instr_name_t.C_J, riscv_instr_name_t.C_JAL,
riscv_instr_name_t.C_BNEZ, riscv_instr_name_t.C_BEQZ]:
self.imm_align = 1
def extend_imm(self):
if self.instr_name != riscv_instr_name_t.C_LUI:
super().extend_imm()
self.imm = self.imm << self.imm_align
def set_rand_mode(self):
if self.format in [riscv_instr_format_t.CR_FORMAT]:
if self.category in [riscv_instr_category_t.JUMP]:
self.has_rd = 0
else:
self.has_rs1 = 0
self.has_imm = 0
elif self.format in [riscv_instr_format_t.CI_FORMAT, riscv_instr_format_t.CIW_FORMAT]:
self.has_rs2 = 0
self.has_rs1 = 0
elif self.format in [riscv_instr_format_t.CSS_FORMAT]:
self.has_rs1 = 0
self.has_rd = 0
elif self.format in [riscv_instr_format_t.CL_FORMAT]:
self.has_rs2 = 0
elif self.format in [riscv_instr_format_t.CS_FORMAT]:
self.has_rd = 0
elif self.format in [riscv_instr_format_t.CA_FORMAT]:
self.has_rs1 = 0
self.has_imm = 0
elif self.format == riscv_instr_format_t.CJ_FORMAT:
self.has_rs1 = 0
self.has_rs2 = 0
self.has_rd = 0
elif self.format == riscv_instr_format_t.CB_FORMAT:
if self.instr_name != riscv_instr_name_t.C_ANDI:
self.has_rd = 0
self.has_rs2 = 0
def convert2asm(self, prefix=""):
asm_str = pkg_ins.format_string(string=self.get_instr_name(),
length=pkg_ins.MAX_INSTR_STR_LEN)
if self.category != riscv_instr_category_t.SYSTEM:
if self.format in [riscv_instr_format_t.CI_FORMAT, riscv_instr_format_t.CIW_FORMAT]:
if self.instr_name is riscv_instr_name_t.C_NOP:
asm_str = "c.nop"
elif self.instr_name is riscv_instr_name_t.C_ADDI16SP:
asm_str = "{} sp, {}".format(asm_str, self.get_imm())
elif self.instr_name is riscv_instr_name_t.C_ADDI4SPN:
asm_str = "{} {}, sp, {}".format(asm_str, self.rd.name, self.get_imm())
elif self.instr_name in [riscv_instr_name_t.C_LDSP, riscv_instr_name_t.C_LWSP,
riscv_instr_name_t.C_LQSP]:
asm_str = "{} {}, {}(sp)".format(asm_str, self.rd.name, self.get_imm())
else:
asm_str = "{} {}, {}".format(asm_str, self.rd.name, self.get_imm())
elif self.format is riscv_instr_format_t.CL_FORMAT:
asm_str = "{} {}, {}({})".format(
asm_str, self.rd.name, self.get_imm(), self.rs1.name)
elif self.format is riscv_instr_format_t.CS_FORMAT:
if self.category is riscv_instr_category_t.STORE:
asm_str = "{} {}, {}({})".format(
asm_str, self.rs2.name, self.get_imm(), self.rs1.name)
else:
asm_str = "{} {}, {}".format(asm_str, self.rs1.name, self.rs2.name)
elif self.format is riscv_instr_format_t.CA_FORMAT:
asm_str = "{} {}, {}".format(asm_str, self.rd.name, self.rs2.name)
elif self.format is riscv_instr_format_t.CB_FORMAT:
asm_str = "{} {}, {}".format(asm_str, self.rs1.name, self.get_imm())
elif self.format is riscv_instr_format_t.CSS_FORMAT:
if self.category is riscv_instr_category_t.STORE:
asm_str = "{} {}, {}(sp)".format(asm_str, self.rs2.name, self.get_imm())
else:
asm_str = "{} {}, {}".format(asm_str, self.rs2.name, self.get_imm())
elif self.format is riscv_instr_format_t.CR_FORMAT:
if self.instr_name in [riscv_instr_name_t.C_JR, riscv_instr_name_t.C_JALR]:
asm_str = "{} {}".format(asm_str, self.rs1.name)
else:
asm_str = "{} {}, {}".format(asm_str, self.rd.name, self.rs2.name)
elif self.format is riscv_instr_format_t.CJ_FORMAT:
asm_str = "{} {}".format(asm_str, self.get_imm())
else:
logging.info("Unsupported format {}".format(self.format.name))
else:
# For EBREAK,C.EBREAK, making sure pc+4 is a valid instruction boundary
# This is needed to resume execution from epc+4 after ebreak handling
if self.instr_name is riscv_instr_name_t.C_EBREAK:
asm_str = "c.ebreak;c.nop;"
if self.comment != "":
asm_str = asm_str + " #" + self.comment
return asm_str.lower()
# TODO
def conver2bin(self, prefix=""):
pass
# TODO
def get_c_opcode(self):
pass
# TOD0
def get_func3(self):
pass

View file

@ -17,11 +17,12 @@ import os
import sys
import vsc
import logging
from importlib import import_module
from enum import Enum, IntEnum, auto
from bitstring import BitArray
from pygen.pygen_src.target.rv32i import riscv_core_setting as rcs
from pygen.pygen_src.riscv_instr_pkg import *
from pygen_src.riscv_instr_pkg import *
from pygen_src.riscv_instr_gen_config import cfg
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
class operand_sign_e(IntEnum):
POSITIVE = 0

View file

@ -11,50 +11,63 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import logging
import copy
import sys
import random
import os
import vsc
from imp import reload
from collections import defaultdict
from bitstring import BitArray
from pygen_src.riscv_instr_pkg import pkg_ins, riscv_reg_t, riscv_instr_name_t
from pygen_src.isa import rv32i_instr # NOQA
from pygen_src.target.rv32i import riscv_core_setting as rcs
logging.basicConfig(filename = os.path.abspath('../test/out/logname.log'), filemode ='w',
format = "%(asctime)s %(filename)s %(lineno)s %(levelname)s %(message)s",
level = logging.DEBUG)
from importlib import import_module
from pygen_src.riscv_instr_pkg import (pkg_ins, riscv_instr_category_t, riscv_reg_t,
riscv_instr_name_t, riscv_instr_format_t,
riscv_instr_group_t, imm_t)
from pygen_src.riscv_instr_gen_config import cfg
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
reload(logging)
logging.basicConfig(filename='{}'.format(cfg.argv.log_file_name),
filemode='w',
format="%(asctime)s %(filename)s %(lineno)s %(levelname)s %(message)s",
level=logging.DEBUG)
@vsc.randobj
class riscv_instr:
# All derived instructions
instr_registry = {}
# Instruction list
instr_names = []
# Categorized instruction list
instr_group = defaultdict(list)
instr_category = defaultdict(list)
basic_instr = []
instr_template = {}
# Privileged CSR filter
exclude_reg = []
include_reg = []
def __init__(self):
self.instr_names = []
self.instr_group = defaultdict(list)
self.instr_category = defaultdict(list)
self.basic_instr = []
self.instr_template = {}
self.exclude_reg = []
self.include_reg = []
self.group = None
self.format = None
self.category = None
self.instr_name = None
self.imm_type = None
self.imm_len = 0
# Instruction attributes
self.group = vsc.enum_t(riscv_instr_group_t)
self.format = vsc.enum_t(riscv_instr_format_t)
self.category = vsc.enum_t(riscv_instr_category_t)
self.instr_name = vsc.enum_t(riscv_instr_name_t)
self.imm_type = vsc.enum_t(imm_t)
self.imm_len = vsc.bit_t(5)
# Operands
self.csr = vsc.rand_bit_t(12)
self.rs2 = vsc.rand_enum_t(riscv_reg_t)
self.rs1 = vsc.rand_enum_t(riscv_reg_t)
self.rd = vsc.rand_enum_t(riscv_reg_t)
self.imm = vsc.rand_bit_t(32)
# Helper Fields
self.imm_mask = vsc.uint32_t(0xffffffff)
self.is_branch_target = None
self.has_label = 1
@ -70,99 +83,123 @@ class riscv_instr:
self.label = ""
self.is_local_numeric_label = None
self.idx = -1
self.has_rs1 = vsc.bit_t(1)
self.has_rs2 = vsc.bit_t(1)
self.has_rd = vsc.bit_t(1)
self.has_imm = vsc.bit_t(1)
self.has_rs1 = 1
self.has_rs2 = 1
self.has_rd = 1
self.has_imm = 1
self.shift_t = vsc.uint32_t(0xffffffff)
self.XLEN = vsc.uint32_t(32) # XLEN is used in constraint throughout the generator.
# Hence, XLEN should be of PyVSC type in order to use it in a constraint block
self.XLEN = rcs.XLEN
@vsc.constraint
def imm_c(self):
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.SLLIW,
riscv_instr_name_t.SRLIW,
riscv_instr_name_t.SRAIW))):
self.imm[11:5] == 0
with vsc.implies(self.instr_name.inside(vsc.rangelist(riscv_instr_name_t.SLLI,
riscv_instr_name_t.SRLI,
riscv_instr_name_t.SRAI))):
with vsc.implies(self.XLEN == 32):
self.imm[11:5] == 0
with vsc.implies(self.XLEN != 32):
self.imm[11:6] == 0
@classmethod
def register(cls, instr_name):
logging.info("Registering %s", instr_name.name)
cls.instr_registry[instr_name.name] = 1
if(instr_name is None):
cls.instr_registry[instr_name] = 1
if instr_name is None:
print("\n")
return 1
def create_instr_list(self, cfg):
self.instr_names.clear()
self.instr_group.clear()
self.instr_category.clear()
for instr_name, values in self.instr_registry.items():
if(instr_name in rcs.unsupported_instr):
@classmethod
def create_instr_list(cls, cfg):
cls.instr_names.clear()
cls.instr_group.clear()
cls.instr_category.clear()
for instr_name, values in cls.instr_registry.items():
if instr_name in rcs.unsupported_instr:
continue
instr_inst = self.create_instr(instr_name)
self.instr_template[instr_name] = instr_inst
instr_inst = cls.create_instr(instr_name)
cls.instr_template[instr_name] = instr_inst
if (not instr_inst.is_supported(cfg)):
if not instr_inst.is_supported(cfg):
continue
if ((rcs.XLEN != 32) and (instr_name == "C_JAL")):
if ((rcs.XLEN != 32) and (instr_name == riscv_instr_name_t.C_JAL)):
continue
if ((riscv_reg_t.SP in cfg.reserved_regs) and (instr_name == "C_ADDI16SP")):
if ((riscv_reg_t.SP in cfg.reserved_regs) and
(instr_name == riscv_instr_name_t.C_ADDI16SP)):
continue
if (cfg.enable_sfence and instr_name == "SFENCE_VMA"):
if (cfg.enable_sfence and instr_name == riscv_instr_name_t.SFENCE_VMA):
continue
if (instr_name in ["FENCE", "FENCE_I", "SFENCE_VMA"]):
if instr_name in [riscv_instr_name_t.FENCE, riscv_instr_name_t.FENCE_I,
riscv_instr_name_t.SFENCE_VMA]:
continue
if (instr_inst.group.name in rcs.supported_isa and
not(cfg.disable_compressed_instr and
instr_inst.group in ["RV32C", "RV64C", "RV32DC", "RV32FC", "RV128C"]) and
not(not(cfg.enable_floating_point) and instr_inst.group in
["RV32F", "RV64F", "RV32D", "RV64D"])):
self.instr_category[instr_inst.category.name].append(instr_name)
self.instr_group[instr_inst.group.name].append(instr_name)
self.instr_names.append(instr_name)
self.build_basic_instruction_list(cfg)
self.create_csr_filter(cfg)
instr_inst.group.name in ["RV32C", "RV64C", "RV32DC", "RV32FC", "RV128C"]) and
not(not(cfg.enable_floating_point) and instr_inst.group.name in
["RV32F", "RV64F", "RV32D", "RV64D"])):
cls.instr_category[instr_inst.category.name].append(instr_name)
cls.instr_group[instr_inst.group.name].append(instr_name)
cls.instr_names.append(instr_name)
cls.build_basic_instruction_list(cfg)
cls.create_csr_filter(cfg)
def create_instr(self, instr_name):
"""TODO This method is specific to RV32I instruction only.
It must be scaled to all instruction extensions."""
try:
instr_inst = eval("rv32i_instr.riscv_" + instr_name + "_instr()")
except Exception:
logging.critical("Failed to create instr: %0s", instr_name)
sys.exit(1)
@classmethod
def create_instr(cls, instr_name):
# Importing get_object here to eliminate the Cyclic dependency for DEFINE_INSTR
from pygen_src.riscv_utils import get_object
instr_inst = get_object(instr_name)
return instr_inst
def is_supported(self, cfg):
return 1
def build_basic_instruction_list(self, cfg):
self.basic_instr = (self.instr_category["SHIFT"] + self.instr_category["ARITHMETIC"] +
self.instr_category["LOGICAL"] + self.instr_category["COMPARE"])
if(cfg.no_ebreak == 0):
self.basic_instr.append("EBREAK")
@classmethod
def build_basic_instruction_list(cls, cfg):
cls.basic_instr = (cls.instr_category["SHIFT"] + cls.instr_category["ARITHMETIC"] +
cls.instr_category["LOGICAL"] + cls.instr_category["COMPARE"])
if cfg.no_ebreak == 0:
cls.basic_instr.append("EBREAK")
for items in rcs.supported_isa:
if("RV32C" in rcs.supported_isa and not(cfg.disable_compressed_instr)):
self.basic_instr.append("C_EBREAK")
cls.basic_instr.append("C_EBREAK")
break
if(cfg.no_dret == 0):
self.basic_instr.append("DRET")
if(cfg.no_fence == 0):
self.basic_instr.append(self.instr_category["SYNCH"])
if cfg.no_dret == 0:
cls.basic_instr.append("DRET")
if cfg.no_fence == 0:
cls.basic_instr.append(cls.instr_category["SYNCH"])
if(cfg.no_csr_instr == 0 and cfg.init_privileged_mode == "MACHINE_MODE"):
self.basic_instr.append(self.instr_category["CSR"])
if(cfg.no_wfi == 0):
self.basic_instr.append("WFI")
cls.basic_instr.append(cls.instr_category["CSR"])
if cfg.no_wfi == 0:
cls.basic_instr.append("WFI")
def create_csr_filter(self, cfg):
self.include_reg.clear()
self.exclude_reg.clear()
@classmethod
def create_csr_filter(cls, cfg):
cls.include_reg.clear()
cls.exclude_reg.clear()
if(cfg.enable_illegal_csr_instruction):
self.exclude_reg = rcs.implemented_csr
elif(cfg.enable_access_invalid_csr_level):
self.include_reg = cfg.invalid_priv_mode_csrs
if cfg.enable_illegal_csr_instruction:
cls.exclude_reg = rcs.implemented_csr
elif cfg.enable_access_invalid_csr_level:
cls.include_reg = cfg.invalid_priv_mode_csrs
else:
if(cfg.init_privileged_mode == "MACHINE_MODE"): # Machine Mode
self.include_reg.append("MSCRATCH")
elif(cfg.init_privileged_mode == "SUPERVISOR_MODE"): # Supervisor Mode
self.include_reg.append("SSCRATCH")
else: # User Mode
self.include_reg.append("USCRATCH")
if cfg.init_privileged_mode == "MACHINE_MODE": # Machine Mode
cls.include_reg.append("MSCRATCH")
elif cfg.init_privileged_mode == "SUPERVISOR_MODE": # Supervisor Mode
cls.include_reg.append("SSCRATCH")
else: # User Mode
cls.include_reg.append("USCRATCH")
def get_rand_instr(self, include_instr=[], exclude_instr=[],
@classmethod
def get_rand_instr(cls, include_instr=[], exclude_instr=[],
include_category=[], exclude_category=[],
include_group=[], exclude_group=[]):
idx = BitArray(uint = 0, length = 32)
@ -170,41 +207,43 @@ class riscv_instr:
allowed_instr = []
disallowed_instr = []
# allowed_categories = []
for items in include_category:
allowed_instr.extend(self.instr_category[items])
allowed_instr.extend(cls.instr_category[items])
for items in exclude_category:
if(items in self.instr_category):
disallowed_instr.extend(self.instr_category[items])
if items in cls.instr_category:
disallowed_instr.extend(cls.instr_category[items])
for items in include_group:
allowed_instr.extend(self.instr_group[items])
allowed_instr.extend(cls.instr_group[items])
for items in exclude_group:
if(items in self.instr_group):
disallowed_instr.extend(self.instr_group[items])
if items in cls.instr_group:
disallowed_instr.extend(cls.instr_group[items])
disallowed_instr.extend(exclude_instr)
# TODO Randomization logic needs to be frame with PyVSC library
if(len(disallowed_instr) == 0):
if len(disallowed_instr) == 0:
try:
if(len(include_instr) > 0):
idx = random.randrange(0, len(include_instr) - 1)
if len(include_instr) > 0:
if len(include_instr) == 1:
idx = 0
else:
idx = random.randrange(0, len(include_instr) - 1)
name = include_instr[idx]
elif(len(allowed_instr) > 0):
elif len(allowed_instr) > 0:
idx = random.randrange(0, len(allowed_instr) - 1)
name = allowed_instr[idx]
else:
idx = random.randrange(0, len(self.instr_names) - 1)
name = self.instr_names[idx]
idx = random.randrange(0, len(cls.instr_names) - 1)
name = cls.instr_names[idx]
except Exception:
logging.critical("[%s] Cannot generate random instruction", riscv_instr.__name__)
sys.exit(1)
else:
try:
name = random.choice(self.instr_names)
if(len(include_instr) > 0):
name = random.choice(cls.instr_names)
if len(include_instr) > 0:
name = random.choice(include_instr)
if(len(allowed_instr) > 0):
if len(allowed_instr) > 0:
name = random.choice(allowed_instr)
except Exception:
logging.critical("[%s] Cannot generate random instruction", riscv_instr.__name__)
@ -212,52 +251,60 @@ class riscv_instr:
# rs1 rs2 values are overwriting and the last generated values are
# getting assigned for a particular instruction hence creating different
# object address and id to ratain the randomly generated values.
instr_h = copy.deepcopy(self.instr_template[name])
instr_h = copy.deepcopy(cls.instr_template[name])
return instr_h
def get_load_store_instr(self, load_store_instr):
@classmethod
def get_load_store_instr(cls, load_store_instr):
instr_h = riscv_instr()
if(len(load_store_instr) == 0):
load_store_instr = self.instr_category["LOAD"] + \
self.instr_category["STORE"]
self.idx = random.randrange(0, len(load_store_instr) - 1)
name = load_store_instr[self.idx]
instr_h = copy(self.instr_template[name])
if len(load_store_instr) == 0:
load_store_instr = cls.instr_category["LOAD"] + \
cls.instr_category["STORE"]
cls.idx = random.randrange(0, len(load_store_instr) - 1)
name = load_store_instr[cls.idx]
instr_h = copy.copy(cls.instr_template[name])
return instr_h
def get_instr(self, name):
if (not self.instr_template.get(name)):
@classmethod
def get_instr(cls, name):
if not cls.instr_template.get(name):
logging.critical("Cannot get instr %s", name)
sys.exit(1)
instr_h = copy.copy(self.instr_template[name])
instr_h = copy.copy(cls.instr_template[name])
return instr_h
def set_rand_mode(self):
# rand_mode setting for Instruction Format
if(self.format.name == "R_FORMAT"):
if self.format.name == "R_FORMAT":
self.has_imm = 0
if(self.format.name == "I_FORMAT"):
if self.format.name == "I_FORMAT":
self.has_rs2 = 0
if(self.format.name in ["S_FORMAT", "B_FORMAT"]):
if self.format.name in ["S_FORMAT", "B_FORMAT"]:
self.has_rd = 0
if(self.format.name in ["U_FORMAT", "J_FORMAT"]):
if self.format.name in ["U_FORMAT", "J_FORMAT"]:
self.has_rs1 = 0
self.has_rs2 = 0
# rand_mode setting for Instruction Category
if(self.category.name == "CSR"):
if self.category.name == "CSR":
self.has_rs2 = 0
if(self.format.name == "I_FORMAT"):
if self.format.name == "I_FORMAT":
self.has_rs1 = 0
def pre_randomize(self):
pass # TODO
with vsc.raw_mode():
self.rs1.rand_mode = bool(self.has_rs1)
self.rs2.rand_mode = bool(self.has_rs2)
self.rd.rand_mode = bool(self.has_rd)
self.imm.rand_mode = bool(self.has_imm)
if self.category != riscv_instr_category_t.CSR:
self.csr.rand_mode = False
def set_imm_len(self):
if(self.format.name in ["U_FORMAT", "J_FORMAT"]):
if self.format.name in ["U_FORMAT", "J_FORMAT"]:
self.imm_len = 20
elif(self.format.name in ["I_FORMAT", "S_FORMAT", "B_FORMAT"]):
if(self.imm_type.name == "UIMM"):
elif self.format.name in ["I_FORMAT", "S_FORMAT", "B_FORMAT"]:
if self.imm_type.name == "UIMM":
self.imm_len = 5
else:
self.imm_len = 11
@ -274,174 +321,158 @@ class riscv_instr:
(self.imm_type.name in ["UIMM", "NZUIMM"]))):
self.imm = self.imm_mask | self.imm
def imm_c(self):
imm_t = BitArray(uint = self.imm, length = 32)
if self.instr_name in [riscv_instr_name_t.SLLIW.name, riscv_instr_name_t.SRLIW.name,
riscv_instr_name_t.SRAIW.name]:
imm_t[20:27:1] = 0
self.imm = imm_t.uint
if self.instr_name in [riscv_instr_name_t.SLLI.name, riscv_instr_name_t.SRLI.name,
riscv_instr_name_t.SRAI.name]:
if rcs.XLEN == 32:
imm_t[20:27:1] = 0
self.imm = imm_t.uint
else:
self.imm_t[20:26:1] = 0
self.imm = imm_t.uint
def post_randomize(self):
self.imm_c()
self.extend_imm()
self.update_imm_str()
def convert2asm(self, prefix = " "):
asm_str = pkg_ins.format_string(string = self.get_instr_name(),
length = pkg_ins.MAX_INSTR_STR_LEN)
if(self.category.name != "SYSTEM"):
if self.format.name == "J_FORMAT":
if self.category != riscv_instr_category_t.SYSTEM:
if self.format == riscv_instr_format_t.J_FORMAT:
asm_str = '{} {}, {}'.format(asm_str, self.rd.name, self.get_imm())
elif self.format.name == "U_FORMAT":
elif self.format == riscv_instr_format_t.U_FORMAT:
asm_str = '{} {}, {}'.format(asm_str, self.rd.name, self.get_imm())
elif self.format.name == "I_FORMAT":
if(self.instr_name == "NOP"):
elif self.format == riscv_instr_format_t.I_FORMAT:
if self.instr_name == riscv_instr_name_t.NOP:
asm_str = "nop"
elif(self.instr_name == "WFI"):
elif self.instr_name == riscv_instr_name_t.WFI:
asm_str = "wfi"
elif(self.instr_name == "FENCE"):
elif self.instr_name == riscv_instr_name_t.FENCE:
asm_str = "fence"
elif(self.instr_name == "FENCE_I"):
elif self.instr_name == riscv_instr_name_t.FENCE_I:
asm_str = "fence.i"
elif(self.category.name == "LOAD"):
elif self.category == riscv_instr_category_t.LOAD:
asm_str = '{} {}, {} ({})'.format(
asm_str, self.rd.name, self.get_imm(), self.rs1.name)
elif(self.category.name == "CSR"):
elif self.category == riscv_instr_category_t.CSR:
asm_str = '{} {}, 0x{}, {}'.format(
asm_str, self.rd.name, self.csr, self.get_imm())
else:
asm_str = '{} {}, {}, {}'.format(
asm_str, self.rd.name, self.rs1.name, self.get_imm())
elif self.format.name == "S_FORMAT":
if(self.category.name == "STORE"):
elif self.format == riscv_instr_format_t.S_FORMAT:
if self.category == riscv_instr_category_t.STORE:
asm_str = '{} {}, {} ({})'.format(
asm_str, self.rs2.name, self.get_imm(), self.rs1.name)
else:
asm_str = '{} {}, {}, {}'.format(
asm_str, self.rs1.name, self.rs2.name, self.get_imm())
elif self.format.name == "B_FORMAT":
if(self.category.name == "STORE"):
elif self.format == riscv_instr_format_t.B_FORMAT:
if self.category == riscv_instr_category_t.STORE:
asm_str = '{} {}, {} ({})'.format(
asm_str, self.rs2.name, self.get_imm(), self.rs1.name)
else:
asm_str = '{} {}, {}, {}'.format(
asm_str, self.rs1.name, self.rs2.name, self.get_imm())
elif self.format.name == "R_FORMAT":
if(self.category.name == "CSR"):
elif self.format == riscv_instr_format_t.R_FORMAT:
if self.category == riscv_instr_category_t.CSR:
asm_str = '{} {}, 0x{}, {}'.format(
asm_str, self.rd.name, self.csr, self.rs1.name)
elif(self.instr_name == "SFENCE_VMA"):
elif self.instr_name == riscv_instr_name_t.SFENCE_VMA:
asm_str = "sfence.vma x0, x0"
else:
asm_str = '{} {}, {}, {}'.format(
asm_str, self.rd.name, self.rs1.name, self.rs2.name)
else:
asm_str = 'Fatal_unsupported_format: {} {}'.format(
self.format.name, self.instr_name)
self.format.name, self.instr_name.name)
else:
if(self.instr_name == "EBREAK"):
if self.instr_name == riscv_instr_name_t.EBREAK:
asm_str = ".4byte 0x00100073 # ebreak"
if(self.comment != ""):
if self.comment != "":
asm_str = asm_str + " #" + self.comment
return asm_str.lower()
def get_opcode(self):
if(self.instr_name == "LUI"):
if self.instr_name == "LUI":
return (BitArray(uint = 55, length = 7).bin)
elif(self.instr_name == "AUIPC"):
elif self.instr_name == "AUIPC":
return (BitArray(uint = 23, length = 7).bin)
elif(self.instr_name == "JAL"):
elif self.instr_name == "JAL":
return (BitArray(uint = 23, length = 7).bin)
elif(self.instr_name == "JALR"):
elif self.instr_name == "JALR":
return (BitArray(uint = 111, length = 7).bin)
elif(self.instr_name in ["BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU"]):
elif self.instr_name in ["BEQ", "BNE", "BLT", "BGE", "BLTU", "BGEU"]:
return (BitArray(uint = 103, length = 7).bin)
elif(self.instr_name in ["LB", "LH", "LW", "LBU", "LHU", "LWU", "LD"]):
elif self.instr_name in ["LB", "LH", "LW", "LBU", "LHU", "LWU", "LD"]:
return (BitArray(uint = 99, length = 7).bin)
elif(self.instr_name in ["SB", "SH", "SW", "SD"]):
elif self.instr_name in ["SB", "SH", "SW", "SD"]:
return (BitArray(uint = 35, length = 7).bin)
elif(self.instr_name in ["ADDI", "SLTI", "SLTIU", "XORI", "ORI", "ANDI",
"SLLI", "SRLI", "SRAI", "NOP"]):
elif self.instr_name in ["ADDI", "SLTI", "SLTIU", "XORI", "ORI", "ANDI",
"SLLI", "SRLI", "SRAI", "NOP"]:
return (BitArray(uint = 19, length = 7).bin)
elif(self.instr_name in ["ADD", "SUB", "SLL", "SLT", "SLTU", "XOR", "SRL",
elif self.instr_name in ["ADD", "SUB", "SLL", "SLT", "SLTU", "XOR", "SRL",
"SRA", "OR", "AND", "MUL", "MULH", "MULHSU", "MULHU",
"DIV", "DIVU", "REM", "REMU"]):
"DIV", "DIVU", "REM", "REMU"]:
return (BitArray(uint = 51, length = 7).bin)
elif(self.instr_name in ["ADDIW", "SLLIW", "SRLIW", "SRAIW"]):
elif self.instr_name in ["ADDIW", "SLLIW", "SRLIW", "SRAIW"]:
return (BitArray(uint = 27, length = 7).bin)
elif(self.instr_name in ["MULH", "MULHSU", "MULHU", "DIV", "DIVU", "REM", "REMU"]):
elif self.instr_name in ["MULH", "MULHSU", "MULHU", "DIV", "DIVU", "REM", "REMU"]:
return (BitArray(uint = 51, length = 7).bin)
elif(self.instr_name in ["FENCE", "FENCE_I"]):
elif self.instr_name in ["FENCE", "FENCE_I"]:
return (BitArray(uint = 15, length = 7).bin)
elif(self.instr_name in ["ECALL", "EBREAK", "CSRRW", "CSRRS", "CSRRC", "CSRRWI",
"CSRRSI", "CSRRCI"]):
elif self.instr_name in ["ECALL", "EBREAK", "CSRRW", "CSRRS", "CSRRC", "CSRRWI",
"CSRRSI", "CSRRCI"]:
return (BitArray(uint = 115, length = 7).bin)
elif(self.instr_name in ["ADDW", "SUBW", "SLLW", "SRLW", "SRAW", "MULW", "DIVW",
"DIVUW", "REMW", "REMUW"]):
elif self.instr_name in ["ADDW", "SUBW", "SLLW", "SRLW", "SRAW", "MULW", "DIVW",
"DIVUW", "REMW", "REMUW"]:
return (BitArray(uint = 59, length = 7).bin)
elif(self.instr_name in ["ECALL", "EBREAK", "URET", "SRET", "MRET", "DRET", "WFI",
"SFENCE_VMA"]):
elif self.instr_name in ["ECALL", "EBREAK", "URET", "SRET", "MRET", "DRET", "WFI",
"SFENCE_VMA"]:
return (BitArray(uint = 115, length = 7).bin)
else:
logging.critical("Unsupported instruction %0s", self.instr_name)
sys.exit(1)
def get_func3(self):
if(self.instr_name in ["JALR", "BEQ", "LB", "SB", "ADDI", "NOP", "ADD", "SUB",
if self.instr_name in ["JALR", "BEQ", "LB", "SB", "ADDI", "NOP", "ADD", "SUB",
"FENCE", "ECALL", "EBREAK", "ADDIW", "ADDW", "SUBW", "MUL",
"MULW", "ECALL", "EBREAK", "URET", "SRET", "MRET", "DRET",
"WFI", "SFENCE_VMA"]):
"WFI", "SFENCE_VMA"]:
return (BitArray(uint = 0, length = 3).bin)
elif(self.instr_name in ["BNE", "LH", "SH", "SLLI", "SLL", "FENCE_I", "CSRRW", "SLLIW",
"SLLW", "MULH"]):
elif self.instr_name in ["BNE", "LH", "SH", "SLLI", "SLL", "FENCE_I", "CSRRW", "SLLIW",
"SLLW", "MULH"]:
return (BitArray(uint = 1, length = 3).bin)
elif(self.instr_name in ["LW", "SW", "SLTI", "SLT", "CSRRS", "MULHS"]):
elif self.instr_name in ["LW", "SW", "SLTI", "SLT", "CSRRS", "MULHS"]:
return (BitArray(uint = 2, length = 3).bin)
elif(self.instr_name in ["SLTIU", "SLTU", "CSRRC", "LD", "SD", "MULHU"]):
elif self.instr_name in ["SLTIU", "SLTU", "CSRRC", "LD", "SD", "MULHU"]:
return (BitArray(uint = 3, length = 3).bin)
elif(self.instr_name in ["BLT", "LBU", "XORI", "XOR", "DIV", "DIVW"]):
elif self.instr_name in ["BLT", "LBU", "XORI", "XOR", "DIV", "DIVW"]:
return (BitArray(uint = 4, length = 3).bin)
elif(self.instr_name in ["BGE", "LHU", "SRLI", "SRAI", "SRL", "SRA", "CSRRWI", "SRLIW",
elif self.instr_name in ["BGE", "LHU", "SRLI", "SRAI", "SRL", "SRA", "CSRRWI", "SRLIW",
"SRAIW", "SRLW",
"SRAW", "DIVU", "DIVUW"]):
"SRAW", "DIVU", "DIVUW"]:
return (BitArray(uint = 5, length = 3).bin)
elif(self.instr_name in ["BLTU", "ORI", "OR", "CSRRSI", "LWU", "REM", "REMW"]):
elif self.instr_name in ["BLTU", "ORI", "OR", "CSRRSI", "LWU", "REM", "REMW"]:
return (BitArray(uint = 6, length = 3).bin)
elif(self.instr_name in ["BGEU", "ANDI", "AND", "CSRRCI", "REMU", "REMUW"]):
elif self.instr_name in ["BGEU", "ANDI", "AND", "CSRRCI", "REMU", "REMUW"]:
return (BitArray(uint = 7, length = 3).bin)
else:
logging.critical("Unsupported instruction %0s", self.instr_name)
sys.exit(1)
def get_func7(self):
if(self.instr_name in ["SLLI", "SRLI", "ADD", "SLL", "SLT", "SLTU", "XOR",
if self.instr_name in ["SLLI", "SRLI", "ADD", "SLL", "SLT", "SLTU", "XOR",
"SRL", "OR", "AND", "FENCE", "FENCE_I", "SLLIW",
"SRLIW", "ADDW", "SLLW", "SRLW", "ECALL", "EBREAK", "URET"]):
"SRLIW", "ADDW", "SLLW", "SRLW", "ECALL", "EBREAK", "URET"]:
return (BitArray(uint = 0, length = 7).bin)
elif(self.instr_name in ["SUB", "SRA", "SRAIW", "SUBW", "SRAW"]):
elif self.instr_name in ["SUB", "SRA", "SRAIW", "SUBW", "SRAW"]:
return (BitArray(uint = 32, length = 7).bin)
elif(self.instr_name in ["MUL", "MULH", "MULHSU", "MULHU", "DIV", "DIVU", "REM",
"REMU", "MULW", "DIVW", "DIVUW", "REMW", "REMUW"]):
elif self.instr_name in ["MUL", "MULH", "MULHSU", "MULHU", "DIV", "DIVU", "REM",
"REMU", "MULW", "DIVW", "DIVUW", "REMW", "REMUW"]:
return (BitArray(uint = 1, length = 7).bin)
elif(self.instr_name in ["SRET", "WFI"]):
elif self.instr_name in ["SRET", "WFI"]:
return (BitArray(uint = 8, length = 7).bin)
elif(self.instr_name == "MRET"):
elif self.instr_name == "MRET":
return (BitArray(uint = 24, length = 7).bin)
elif(self.instr_name == "DRET"):
elif self.instr_name == "DRET":
return (BitArray(uint = 61, length = 7).bin)
elif(self.instr_name == "SFENCE_VMA"):
elif self.instr_name == "SFENCE_VMA":
return (BitArray(uint = 9, length = 7).bin)
else:
logging.critical("Unsupported instruction %0s", self.instr_name)
@ -451,10 +482,8 @@ class riscv_instr:
pass # TODO
def get_instr_name(self):
get_instr_name = self.instr_name
for i in get_instr_name:
if(i == "_"):
get_instr_name = get_instr_name.replace(i, ".")
get_instr_name = self.instr_name.name
get_instr_name = get_instr_name.replace("_", ".")
return get_instr_name
def get_c_gpr(self, gpr):
@ -479,6 +508,3 @@ class riscv_instr:
else:
signed_x = x - 2 ** rcs.XLEN
return signed_x
riscv_instr_ins = riscv_instr()

View file

@ -0,0 +1,77 @@
"""
Copyright 2020 Google LLC
Copyright 2020 PerfectVIPs Inc.
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.
"""
from pygen_src.riscv_defines import DEFINE_C_INSTR
from pygen_src.riscv_instr_pkg import (riscv_instr_name_t, riscv_instr_format_t,
riscv_instr_category_t, riscv_instr_group_t, imm_t)
DEFINE_C_INSTR(riscv_instr_name_t.C_LW, riscv_instr_format_t.CL_FORMAT,
riscv_instr_category_t.LOAD, riscv_instr_group_t.RV32C, imm_t.UIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SW, riscv_instr_format_t.CS_FORMAT,
riscv_instr_category_t.STORE, riscv_instr_group_t.RV32C, imm_t.UIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_LWSP, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.LOAD, riscv_instr_group_t.RV32C, imm_t.UIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SWSP, riscv_instr_format_t.CSS_FORMAT,
riscv_instr_category_t.STORE, riscv_instr_group_t.RV32C, imm_t.UIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_ADDI4SPN, riscv_instr_format_t.CIW_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, imm_t.NZUIMM,
g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_ADDI, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, imm_t.NZIMM,
g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_ADDI16SP, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, imm_t.NZIMM,
g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_LI, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_LUI, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, imm_t.NZIMM,
g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SUB, riscv_instr_format_t.CA_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_ADD, riscv_instr_format_t.CR_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_NOP, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_MV, riscv_instr_format_t.CR_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_ANDI, riscv_instr_format_t.CB_FORMAT,
riscv_instr_category_t.LOGICAL, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_XOR, riscv_instr_format_t.CA_FORMAT,
riscv_instr_category_t.LOGICAL, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_OR, riscv_instr_format_t.CA_FORMAT,
riscv_instr_category_t.LOGICAL, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_AND, riscv_instr_format_t.CA_FORMAT,
riscv_instr_category_t.LOGICAL, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_BEQZ, riscv_instr_format_t.CB_FORMAT,
riscv_instr_category_t.BRANCH, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_BNEZ, riscv_instr_format_t.CB_FORMAT,
riscv_instr_category_t.BRANCH, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SRLI, riscv_instr_format_t.CB_FORMAT,
riscv_instr_category_t.SHIFT, riscv_instr_group_t.RV32C, imm_t.NZUIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SRAI, riscv_instr_format_t.CB_FORMAT,
riscv_instr_category_t.SHIFT, riscv_instr_group_t.RV32C, imm_t.NZUIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_SLLI, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.SHIFT, riscv_instr_group_t.RV32C, imm_t.NZUIMM, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_J, riscv_instr_format_t.CJ_FORMAT,
riscv_instr_category_t.JUMP, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_JAL, riscv_instr_format_t.CJ_FORMAT,
riscv_instr_category_t.JUMP, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_JR, riscv_instr_format_t.CR_FORMAT,
riscv_instr_category_t.JUMP, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_JALR, riscv_instr_format_t.CR_FORMAT,
riscv_instr_category_t.JUMP, riscv_instr_group_t.RV32C, g=globals())
DEFINE_C_INSTR(riscv_instr_name_t.C_EBREAK, riscv_instr_format_t.CI_FORMAT,
riscv_instr_category_t.SYSTEM, riscv_instr_group_t.RV32C, g=globals())

View file

@ -0,0 +1,35 @@
"""
Copyright 2020 Google LLC
Copyright 2020 PerfectVIPs Inc.
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.
"""
from pygen_src.riscv_defines import DEFINE_INSTR
from pygen_src.riscv_instr_pkg import (riscv_instr_name_t, riscv_instr_format_t,
riscv_instr_category_t, riscv_instr_group_t)
# Multiplication and Division Instructions
DEFINE_INSTR(riscv_instr_name_t.MUL, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.MULH, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.MULHSU, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.MULHU, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.DIV, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.DIVU, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.REM, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.ARITHMETIC, riscv_instr_group_t.RV32M, g=globals())
DEFINE_INSTR(riscv_instr_name_t.REMU, riscv_instr_format_t.R_FORMAT,
riscv_instr_category_t.LOAD, riscv_instr_group_t.RV32M, g=globals())

View file

@ -12,18 +12,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import subprocess
import logging
import random
import copy
import sys
import vsc
from bitstring import BitArray
from importlib import import_module
from pygen_src.riscv_instr_sequence import riscv_instr_sequence
from pygen_src.riscv_instr_pkg import pkg_ins, privileged_reg_t, privileged_mode_t, mtvec_mode_t
from pygen_src.riscv_instr_gen_config import cfg, args, args_dict
from pygen_src.target.rv32i import riscv_core_setting as rcs
from pygen_src.riscv_instr_stream import riscv_rand_instr_stream
from pygen_src.riscv_instr_pkg import (pkg_ins, privileged_reg_t,
privileged_mode_t, mtvec_mode_t,
misa_ext_t, riscv_instr_group_t)
from pygen_src.riscv_instr_gen_config import cfg
from pygen_src.riscv_data_page_gen import riscv_data_page_gen
from pygen_src.riscv_utils import factory
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
'''
RISC-V assembly program generator
@ -42,6 +46,7 @@ class riscv_asm_program_gen:
self.page_table_list = []
self.main_program = []
self.sub_program = []
self.data_page_gen = None
# Main function to generate the whole program
@ -116,8 +121,8 @@ class riscv_asm_program_gen:
self.gen_program_end(hart)
for hart in range(cfg.num_of_harts):
self.gen_data_page_begin(hart)
if(cfg.no_data_page):
self.gen_data_page()
if not cfg.no_data_page:
self.gen_data_page(hart)
if((hart == 0) and ("RV32A" in rcs.supported_isa)):
self.gen_data_page(hart, amo = 1)
@ -137,7 +142,7 @@ class riscv_asm_program_gen:
self.gen_all_trap_handler(hart)
for mode in rcs.supported_privileged_mode:
self.gen_interrupt_handler_section(mode, hart)
self.instr_stream.append(pkg_ins.get_label("kernel_instr_end: nop", hart))
self.gen_kernel_stack_section(hart)
def gen_kernel_program(self, hart, seq):
@ -185,7 +190,9 @@ class riscv_asm_program_gen:
self.instr_stream.append(".align 6; .global fromhost; fromhost: .dword 0;")
def gen_data_page(self, hart, is_kernel = 0, amo = 0):
pass
self.data_page_gen = riscv_data_page_gen()
self.data_page_gen.gen_data_page(hart, cfg.data_page_pattern, is_kernel, amo)
self.instr_stream.extend(self.data_page_gen.data_page_str)
def gen_stack_section(self, hart):
hart_prefix_string = pkg_ins.hart_prefix(hart)
@ -201,7 +208,7 @@ class riscv_asm_program_gen:
self.instr_stream.append(".align 2")
self.instr_stream.append(pkg_ins.get_label("user_stack_start:", hart))
self.instr_stream.append(".rept {}".format(cfg.kernel_stack_len - 1))
self.instr_stream.append(".rept {}".format(cfg.stack_len - 1))
self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8))
self.instr_stream.append(".endr")
self.instr_stream.append(pkg_ins.get_label("user_stack_end:", hart))
@ -250,11 +257,57 @@ class riscv_asm_program_gen:
string = pkg_ins.indent + "j main"
self.instr_stream.append(string)
# Setup MISA based on supported extensions
def setup_misa(self):
# TO DO
misa = 0b01000000
self.instr_stream.append("{}li x{}, {}".format(pkg_ins.indent, cfg.gpr[0].value, hex(misa)))
self.instr_stream.append("{}csrw misa, x{}".format(pkg_ins.indent, cfg.gpr[0].value))
misa = vsc.bit_t(rcs.XLEN)
if rcs.XLEN == 32:
misa[rcs.XLEN - 1:rcs.XLEN - 2] = 1
elif rcs.XLEN == 64:
misa[rcs.XLEN - 1:rcs.XLEN - 2] = 2
else:
misa[rcs.XLEN - 1:rcs.XLEN - 2] = 3
if cfg.check_misa_init_val:
self.instr_stream.append("{}csrr x15, {}".format(pkg_ins.indent,
hex(privileged_reg_t.MISA)))
for i in range(len(rcs.supported_isa)):
if rcs.supported_isa[i] in [riscv_instr_group_t.RV32C.name,
riscv_instr_group_t.RV64C.name,
riscv_instr_group_t.RV128C.name]:
misa[misa_ext_t.MISA_EXT_C] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32I.name,
riscv_instr_group_t.RV64I.name,
riscv_instr_group_t.RV128I.name]:
misa[misa_ext_t.MISA_EXT_I] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32M.name,
riscv_instr_group_t.RV64M.name]:
misa[misa_ext_t.MISA_EXT_M] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32A.name,
riscv_instr_group_t.RV64A.name]:
misa[misa_ext_t.MISA_EXT_A] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32B.name,
riscv_instr_group_t.RV64B.name]:
misa[misa_ext_t.MISA_EXT_B] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32F.name,
riscv_instr_group_t.RV64F.name,
riscv_instr_group_t.RV32FC.name]:
misa[misa_ext_t.MISA_EXT_F] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32D.name,
riscv_instr_group_t.RV64D.name,
riscv_instr_group_t.RV32DC.name]:
misa[misa_ext_t.MISA_EXT_D] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RVV.name]:
misa[misa_ext_t.MISA_EXT_V] = 1
elif rcs.supported_isa[i] in [riscv_instr_group_t.RV32X.name,
riscv_instr_group_t.RV64X.name]:
misa[misa_ext_t.MISA_EXT_X] = 1
else:
logging.error("{} is not yet supported".format(rcs.supported_isa[i]))
if privileged_mode_t.SUPERVISOR_MODE.name in rcs.supported_privileged_mode:
misa[misa_ext_t.MISA_EXT_S] = 1
self.instr_stream.append("{}li x{}, {}".format(pkg_ins.indent, cfg.gpr[0].value,
hex(misa.get_val())))
self.instr_stream.append("{}csrw {}, x{}".format(pkg_ins.indent,
hex(privileged_reg_t.MISA), cfg.gpr[0].value))
def core_is_initialized(self):
pass
@ -264,26 +317,24 @@ class riscv_asm_program_gen:
def init_gpr(self):
reg_val = BitArray(uint = 0, length = pkg_ins.DATA_WIDTH)
dist_lst = []
for dist_val in range(5):
if dist_val == 0:
# TODO Map the function with PyVSC std::randomize()
for i in range(rcs.NUM_GPR):
if i in [cfg.sp.value, cfg.tp.value]:
continue
if i == 0:
reg_val = BitArray(hex='0x0')
elif dist_val == 1:
elif i == 1:
reg_val = BitArray(hex='0x80000000')
elif dist_val == 2:
elif i == 2:
temp = random.randrange(0x1, 0xf)
reg_val = BitArray(hex(temp), length=32)
elif dist_val == 3:
elif i == 3:
temp = random.randrange(0x10, 0xefffffff)
reg_val = BitArray(hex(temp), length=32)
else:
temp = random.randrange(0xf0000000, 0xffffffff)
reg_val = BitArray(hex(temp), length=32)
dist_lst.append(reg_val)
for i in range(32):
init_string = "{}li x{}, {}".format(pkg_ins.indent, i, random.choice(dist_lst))
init_string = "{}li x{}, {}".format(pkg_ins.indent, i, reg_val)
self.instr_stream.append(init_string)
def init_floating_point_gpr(self):
@ -325,7 +376,7 @@ class riscv_asm_program_gen:
string.append("la x{}, {}kernel_stack_end".format(cfg.tp.value, pkg_ins.hart_prefix(hart)))
self.gen_section(pkg_ins.get_label("kernel_sp", hart), string)
if(not cfg.no_delegation and (cfg.init_privileged_mode != "MACHINE_MODE")):
if not cfg.no_delegation and (cfg.init_privileged_mode != privileged_mode_t.MACHINE_MODE):
self.gen_delegation(hart)
self.trap_vector_init(hart)
self.setup_pmp(hart)
@ -436,6 +487,16 @@ class riscv_asm_program_gen:
# TODO
pkg_ins.push_gpr_to_kernel_stack(
status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr)
# Checking xStatus can be optional if ISS (like spike) has different implementation of
# certain fields compared with the RTL processor.
if cfg.check_xstatus:
instr.append("csrr x{}, {} # {}".format(
cfg.gpr[0].value, hex(status.value), status.name))
instr.append("csrr x{}, {} # {}\n".format(cfg.gpr[0].value, hex(cause.value),
cause.name) +
"{}srli x{}, x{}, {}\n".format(pkg_ins.indent, cfg.gpr[0].value,
cfg.gpr[0].value, rcs.XLEN - 1) + "{}bne x{}, x0, {}{}mode_instr_handler"
.format(pkg_ins.indent, cfg.gpr[0].value, pkg_ins.hart_prefix(hart), mode))
# The trap handler will occupy one 4KB page, it will be allocated one entry in
# the page table with a specific privileged mode.
@ -448,6 +509,10 @@ class riscv_asm_program_gen:
self.gen_section(pkg_ins.get_label("{}_handler".format(tvec_name.lower()), hart), instr)
# TODO Exception handler
instr = []
if cfg.mtvec_mode == mtvec_mode_t.VECTORED:
pkg_ins.push_gpr_to_kernel_stack(
status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr)
def gen_interrupt_vector_table(self, hart, mode, status, cause, ie,
ip, scratch, instr):
@ -546,7 +611,7 @@ class riscv_asm_program_gen:
ip.name)]
interrupt_handler_instr.extend(to_extend_interrupt_hanlder_instr)
self.gen_plic_section(interrupt_handler_instr)
pkg_ins.pop_gpr_from_kernel_stack(status.name, scratch.name, cfg.mstatus_mprv,
pkg_ins.pop_gpr_from_kernel_stack(status, scratch, cfg.mstatus_mprv,
cfg.sp, cfg.tp, interrupt_handler_instr)
interrupt_handler_instr.append("%0sret;" % (mode_prefix))
@ -555,7 +620,7 @@ class riscv_asm_program_gen:
else:
self.instr_stream.append(".align 2")
self.gen_section(pkg_ins.get_label("%0smode_intr_handler" %
self.gen_section(pkg_ins.get_label("%0smode_instr_handler" %
(mode_prefix), hart), interrupt_handler_instr)
def format_section(self, instr):
@ -574,8 +639,7 @@ class riscv_asm_program_gen:
pass
def gen_test_file(self, test_name):
subprocess.run(["mkdir", "-p", "out/asm_tests"])
file = open("./out/asm_tests/{}".format(test_name), "w+")
file = open(test_name, "w+")
for items in self.instr_stream:
file.write("{}\n".format(items))
@ -596,17 +660,18 @@ class riscv_asm_program_gen:
arg = "directed_instr_{}".format(i)
stream_name_opts = "stream_name_{}".format(i)
stream_freq_opts = "stream_freq_{}".format(i)
if(arg in args):
val = args_dict[arg]
if cfg.args_dict[arg]:
val = cfg.args_dict[arg]
opts = val.split(",")
if(len(opts) != 2):
if len(opts) != 2:
logging.critical(
"Incorrect directed instruction format : %0s, expect: name,ratio", val)
sys.exit(1)
else:
self.add_directed_instr_stream(opts[0], int(opts[1]))
elif(stream_name_opts in args and stream_freq_opts in args):
stream_name = args_dict[stream_name_opts]
stream_freq = args_dict[stream_freq_opts]
elif cfg.args_dict[stream_name_opts] and cfg.args_dict[stream_freq_opts]:
stream_name = cfg.args_dict[stream_name_opts]
stream_freq = cfg.args_dict[stream_freq_opts]
self.add_directed_instr_stream(stream_name, stream_freq)
def generate_directed_instr_stream(self, hart = 0, label = "", original_instr_cnt = 0,
@ -629,7 +694,7 @@ class riscv_asm_program_gen:
if(object_h is None):
logging.critical("Cannot create instr stream %0s", name)
sys.exit(1)
new_instr_stream = copy.copy(object_h)
new_instr_stream = copy.deepcopy(object_h)
if(new_instr_stream):
new_instr_stream.hart = hart
new_instr_stream.label = "{}_{}".format(label, idx)

View file

@ -0,0 +1,87 @@
"""
Copyright 2020 Google LLC
Copyright 2020 PerfectVIPs Inc.
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.
"""
import logging
import vsc
import random
from collections import defaultdict
from pygen_src.riscv_instr_pkg import pkg_ins, data_pattern_t
from pygen_src.riscv_instr_gen_config import cfg
@vsc.randobj
class riscv_data_page_gen:
def __init__(self):
self.data_page_str = []
self.mem_region_setting = defaultdict(list)
@staticmethod
def gen_data(idx, pattern, num_of_bytes, data):
temp_data = 0
data = [0] * num_of_bytes
for i in range(len(data)):
if pattern == data_pattern_t.RAND_DATA:
temp_data = random.randrange(0, (2**8) - 1)
data[i] = temp_data
elif pattern == data_pattern_t.INCR_VAL:
data[i] = (idx + i) % 256
return data
def gen_data_page(self, hart_id, pattern, is_kernel=0, amo=0):
tmp_str = ""
temp_data = []
tmp_data = []
page_cnt = 0
page_size = 0
self.data_page_str.clear()
if is_kernel:
self.mem_region_setting = cfg.s_mem_region
elif amo:
self.mem_region_setting = cfg.amo_region
else:
self.mem_region_setting = cfg.mem_region
for i in range(len(self.mem_region_setting)):
logging.info("Generate data section: {} size:0x{} xwr:0x{}".format(
self.mem_region_setting[i]["name"],
self.mem_region_setting[i]["size_in_bytes"],
self.mem_region_setting[i]["xwr"]))
if amo:
if cfg.use_push_data_section:
self.data_page_str.append(".pushsection .{},\"aw\",@progbits;"
.format(self.mem_region_setting[i]["name"]))
else:
self.data_page_str.append(".section .{},\"aw\",@progbits;"
.format(self.mem_region_setting[i]["name"]))
self.data_page_str.append("{}:".format(self.mem_region_setting[i]["name"]))
else:
if cfg.use_push_data_section:
self.data_page_str.append(".pushsection .{},\"aw\",@progbits;"
.format(pkg_ins.hart_prefix(hart_id) +
self.mem_region_setting[i]["name"]))
else:
self.data_page_str.append(".section .{},\"aw\",@progbits;"
.format(pkg_ins.hart_prefix(hart_id) +
self.mem_region_setting[i]["name"]))
self.data_page_str.append("{}:".format(pkg_ins.hart_prefix(hart_id) +
self.mem_region_setting[i]["name"]))
page_size = self.mem_region_setting[i]["size_in_bytes"]
for i in range(0, page_size, 32):
if page_size - 1 >= 32:
temp_data = self.gen_data(idx=i, pattern=pattern,
num_of_bytes=32, data=temp_data)
else:
temp_data = self.gen_data(idx=i, pattern=pattern,
num_of_bytes=page_size - 1, data=temp_data)
tmp_str = pkg_ins.format_string(".word {}".format(pkg_ins.format_data(temp_data)),
pkg_ins.LABEL_STR_LEN)
self.data_page_str.append(tmp_str)
if cfg.use_push_data_section:
self.data_page_str.append(".popsection")

View file

@ -14,14 +14,15 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
from pygen_src.riscv_instr_pkg import imm_t
from pygen_src.isa.riscv_instr import riscv_instr
from pygen_src.isa.riscv_compressed_instr import riscv_compressed_instr
def DEFINE_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp=imm_t.IMM, g=globals()):
class_name = f"riscv_{instr_n.name}_instr"
class_name = "riscv_{}_instr".format(instr_n.name)
def __init__(self):
riscv_instr.__init__(self)
self.instr_name = instr_n.name
self.instr_name = instr_n
self.format = instr_format
self.category = instr_category
self.group = instr_group
@ -36,6 +37,25 @@ def DEFINE_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp=imm_
g[class_name] = NewClass
def DEFINE_C_INSTR(instr_n, instr_format, instr_category, instr_group, imm_tp=imm_t.IMM, g=globals()):
class_name = "riscv_{}_instr".format(instr_n.name)
def __init__(self):
riscv_compressed_instr.__init__(self)
self.instr_name = instr_n
self.format = instr_format
self.category = instr_category
self.group = instr_group
self.imm_type = imm_tp
self.set_imm_len()
self.set_rand_mode()
NewClass = type(class_name, (riscv_compressed_instr,), {
"__init__": __init__,
"valid": riscv_compressed_instr.register(instr_n)
})
g[class_name] = NewClass
'''
TODO
@vsc.constraint

View file

@ -11,22 +11,28 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import sys
import random
import logging
import vsc
from importlib import import_module
from enum import IntEnum, auto
from pygen_src.riscv_instr_stream import riscv_rand_instr_stream
from pygen_src.isa.riscv_instr import riscv_instr_ins
from pygen_src.isa.riscv_instr import riscv_instr
from pygen_src.riscv_instr_gen_config import cfg
from pygen_src.riscv_instr_pkg import riscv_reg_t, riscv_pseudo_instr_name_t
from pygen_src.target.rv32i import riscv_core_setting as rcs
from pygen_src.riscv_instr_pkg import (riscv_reg_t,
riscv_pseudo_instr_name_t, riscv_instr_name_t, pkg_ins)
from pygen_src.riscv_pseudo_instr import riscv_pseudo_instr
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
class riscv_directed_instr_stream(riscv_rand_instr_stream):
label = ""
def __init__(self):
super().__init__()
self.name = ""
self.label = ""
def post_randomize(self):
for i in range(len(self.instr_list)):
@ -34,11 +40,74 @@ class riscv_directed_instr_stream(riscv_rand_instr_stream):
self.instr_list[i].atomic = 1
self.instr_list[0].comment = "Start %0s" % (self.name)
self.instr_list[-1].comment = "End %0s" % (self.name)
if self.label != "":
self.instr_list[0].label = self.label
if riscv_directed_instr_stream.label != "":
self.instr_list[0].label = riscv_directed_instr_stream.label
self.instr_list[0].has_label = 1
@vsc.randobj
class riscv_jal_instr(riscv_rand_instr_stream):
def __init__(self):
super().__init__()
self.name = ""
self.jump = []
self.jump_start = riscv_instr()
self.jump_end = riscv_instr()
self.num_of_jump_instr = vsc.rand_int_t()
@vsc.constraint
def instr_c(self):
self.num_of_jump_instr in vsc.rangelist(vsc.rng(10, 30))
def post_randomize(self):
order = []
RA = cfg.ra
order = [0] * self.num_of_jump_instr
self.jump = [0] * self.num_of_jump_instr
for i in range(len(order)):
order[i] = i
random.shuffle(order)
self.setup_allowed_instr(1, 1)
jal = [riscv_instr_name_t.JAL]
if not cfg.disable_compressed_instr:
jal.append(riscv_instr_name_t.C_J)
if rcs.XLEN == 32:
jal.append(riscv_instr_name_t.C_JAL)
self.jump_start = riscv_instr.get_instr(riscv_instr_name_t.JAL.name)
with self.jump_start.randomize_with() as it:
self.jump_start.rd == RA
self.jump_start.imm_str = "{}f".format(order[0])
self.jump_start.label = self.label
# Last instruction
self.jump_end = self.randomize_instr(self.jump_end)
self.jump_end.label = "{}".format(self.num_of_jump_instr)
for i in range(self.num_of_jump_instr):
self.jump[i] = riscv_instr.get_rand_instr(include_instr = [jal[0].name])
with self.jump[i].randomize_with() as it:
if self.jump[i].has_rd:
vsc.dist(self.jump[i].rd, [vsc.weight(riscv_reg_t.RA, 5), vsc.weight(
vsc.rng(riscv_reg_t.SP, riscv_reg_t.T0), 1),
vsc.weight(vsc.rng(riscv_reg_t.T2, riscv_reg_t.T6), 2)])
self.jump[i].rd.not_inside(cfg.reserved_regs)
self.jump[i].label = "{}".format(i)
for i in range(len(order)):
if i == self.num_of_jump_instr - 1:
self.jump[order[i]].imm_str = "{}f".format(self.num_of_jump_instr)
else:
if order[i + 1] > order[i]:
self.jump[order[i]].imm_str = "{}f".format(order[i + 1])
else:
self.jump[order[i]].imm_str = "{}b".format(order[i + 1])
self.instr_list.append(self.jump_start)
self.instr_list.extend(self.jump)
self.instr_list.append(self.jump_end)
for i in range(len(self.instr_list)):
self.instr_list[i].has_label = 1
self.instr_list[i].atomic = 1
class int_numeric_e(IntEnum):
NormalValue = auto()
Zero = auto()
@ -90,9 +159,140 @@ class riscv_int_numeric_corner_stream(riscv_directed_instr_stream):
self.init_instr[i].imm_str = "0x%0x" % (self.init_val[i])
self.instr_list.append(self.init_instr[i])
for i in range(self.num_of_instr):
instr = riscv_instr_ins.get_rand_instr(
instr = riscv_instr.get_rand_instr(
include_category = ['ARITHMETIC'],
exclude_group = ['RV32C', 'RV64C', 'RV32F', 'RV64F', 'RV32D', 'RV64D'])
instr = self.randomize_gpr(instr)
self.instr_list.append(instr)
super().post_randomize()
# Push Stack Instructions
class riscv_push_stack_instr(riscv_rand_instr_stream):
def __init__(self):
super().__init__()
self.stack_len = 0
self.num_of_reg_to_save = 0
self.num_of_redundant_instr = 0
self.push_stack_instr = []
self.saved_regs = []
self.branch_instr = riscv_instr()
self.enable_branch = vsc.rand_bit_t()
self.push_start_label = ''
def init(self):
self.reserved_rd = [cfg.ra]
self.saved_regs = [cfg.ra]
self.num_of_reg_to_save = len(self.saved_regs)
if self.num_of_reg_to_save * (rcs.XLEN / 8) > self.stack_len:
logging.error('stack len [%0d] is not enough to store %d regs',
self.stack_len, self.num_of_reg_to_save)
sys.exit(1)
self.num_of_redundant_instr = random.randrange(3, 10)
self.initialize_instr_list(self.num_of_redundant_instr)
def gen_push_stack_instr(self, stack_len, allow_branch=1):
self.stack_len = stack_len
self.init()
self.push_stack_instr = [0] * (self.num_of_reg_to_save + 1)
for i in range(len(self.push_stack_instr)):
self.push_stack_instr[i] = riscv_instr()
self.push_stack_instr[0] = \
riscv_instr.get_instr(riscv_instr_name_t.ADDI.name)
with self.push_stack_instr[0].randomize_with() as it:
self.push_stack_instr[0].rd == cfg.sp
self.push_stack_instr[0].rs1 == cfg.sp
self.push_stack_instr[0].imm == (~cfg.stack_len) + 1
self.push_stack_instr[0].imm_str = '-{}'.format(self.stack_len)
for i in range(len(self.saved_regs)):
if rcs.XLEN == 32:
self.push_stack_instr[i + 1] = riscv_instr.get_instr(riscv_instr_name_t.SW.name)
with self.push_stack_instr[i + 1].randomize_with() as it:
self.push_stack_instr[i + 1].rs2 == self.saved_regs[i]
self.push_stack_instr[i + 1].rs1 == cfg.sp
self.push_stack_instr[i + 1].imm == 4 * (i + 1)
else:
self.push_stack_instr[i + 1] = riscv_instr.get_instr(riscv_instr_name_t.SD.name)
with self.push_stack_instr[i + 1].randomize_with() as it:
self.push_stack_instr[i + 1].rs2 == self.saved_regs[i]
self.push_stack_instr[i + 1].rs1 == cfg.sp
self.push_stack_instr[i + 1].imm == 8 * (i + 1)
self.push_stack_instr[i + 1].process_load_store = 0
if allow_branch:
# TODO `DV_CHECK_STD_RANDOMIZE_FATAL(enable_branch)
pass
else:
self.enable_branch = 0
if self.enable_branch:
self.branch_instr = \
riscv_instr.get_rand_instr(include_category=[riscv_instr_name_t.BRANCH.name])
# `DV_CHECK_STD_RANDOMIZE_FATAL(branch_instr)
self.branch_instr.imm_str = self.push_start_label
self.branch_instr.brach_assigned = 1
self.push_stack_instr[0].label = self.push_start_label
self.push_stack_instr[0].has_label = 1
self.branch_instr.extend(self.push_stack_instr)
self.mix_instr_stream(self.push_stack_instr)
for i in range(len(self.instr_list)):
self.instr_list[i].atomic = 1
if self.instr_list[i].label == '':
self.instr_list[i].has_label = 0
# Pop stack instruction stream
class riscv_pop_stack_instr(riscv_rand_instr_stream):
def __init__(self):
super().__init__()
self.stack_len = 0
self.num_of_reg_to_save = 0
self.num_of_redundant_instr = 0
self.pop_stack_instr = []
self.saved_regs = []
def init(self):
self.reserved_rd = [cfg.ra]
self.num_of_reg_to_save = len(self.saved_regs)
if self.num_of_reg_to_save * 4 > self.stack_len:
logging.error('stack len [%0d] is not enough to store %d regs',
self.stack_len, self.num_of_reg_to_save)
sys.exit(1)
self.num_of_redundant_instr = random.randrange(3, 10)
self.initialize_instr_list(self.num_of_redundant_instr)
def gen_pop_stack_instr(self, stack_len, saved_regs):
self.stack_len = stack_len
self.saved_regs = saved_regs
self.init()
self.gen_instr(1)
self.pop_stack_instr = [None] * (self.num_of_reg_to_save + 1)
for i in range(len(self.pop_stack_instr)):
self.pop_stack_instr[i] = riscv_instr()
for i in range(len(self.saved_regs)):
if rcs.XLEN == 32:
self.pop_stack_instr[i] = riscv_instr.get_instr(riscv_instr_name_t.LW.name)
with self.pop_stack_instr[i].randomize_with() as it:
self.rd == self.saved_regs[i]
self.rs1 == cfg.sp
self.imm == 4 * (i + 1)
else:
self.pop_stack_instr[i] = riscv_instr.get_instr(riscv_instr_name_t.LD.name)
with self.pop_stack_instr[i].randomize_with() as it:
self.rd == self.saved_regs[i]
self.rs1 == cfg.sp
self.imm == 8 * (i + 1)
self.pop_stack_instr[i].process_load_store = 0
# addi sp,sp,imm
''' TODO `DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[num_of_reg_to_save],
rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;) '''
self.pop_stack_instr[self.num_of_reg_to_save] = riscv_instr.get_instr(
riscv_instr_name_t.ADDI.name)
self.pop_stack_instr[self.num_of_reg_to_save].imm_str = pkg_ins.format_string(
'{}', self.stack_len)
self.mix_instr_stream(self.pop_stack_instr)
for i in range(len(self.instr_list)):
self.instr_list[i].atomic = 1
self.instr_list[i].has_label = 0

File diff suppressed because it is too large Load diff

View file

@ -17,24 +17,27 @@ import logging
import sys
import vsc
from bitstring import BitArray
from pygen_src.riscv_instr_pkg import mtvec_mode_t, f_rounding_mode_t, \
riscv_reg_t, privileged_mode_t, \
riscv_instr_group_t
from pygen_src.target.rv32i import riscv_core_setting as rcs
from importlib import import_module
from pygen_src.riscv_instr_pkg import (mtvec_mode_t, f_rounding_mode_t,
riscv_reg_t, privileged_mode_t,
riscv_instr_group_t, data_pattern_t)
@vsc.randobj
class riscv_instr_gen_config:
def __init__(self, argv):
def __init__(self):
# TODO Support for command line argument
self.main_program_instr_cnt = 100 # count of main_prog
self.sub_program_instr_cnt = [] # count of sub_prog
self.debug_program_instr_cnt = 0 # count of debug_rom
self.debug_sub_program_instr_cnt = [] # count of debug sub_progrms
# Commenting out for now
# self.data_page_pattern = list(
# map(lambda dta_pg: dta_pg.name, data_pattern_t))
# dicts for exception_cause_t & interrupt_cause_t Enum classes
self.max_directed_instr_stream_seq = 20
self.data_page_pattern = vsc.rand_enum_t(data_pattern_t)
self.argv = self.parse_args()
self.args_dict = vars(self.argv)
global rcs
rcs = import_module("pygen_src.target." + self.argv.target + ".riscv_core_setting")
self.m_mode_exception_delegation = {}
self.s_mode_exception_delegation = {}
@ -59,7 +62,7 @@ class riscv_instr_gen_config:
self.mstatus_vs = BitArray(bin(0b0), length=2)
self.mtvec_mode = vsc.rand_enum_t(mtvec_mode_t)
self.tvec_alignment = argv.tvec_alignment
self.tvec_alignment = vsc.rand_uint8_t(self.argv.tvec_alignment)
self.fcsr_rm = list(map(lambda csr_rm: csr_rm.name, f_rounding_mode_t))
self.enable_sfence = 0
@ -83,99 +86,114 @@ class riscv_instr_gen_config:
# Commenting out for now
# vector_cfg = riscv_vector_cfg # TODO
# pmp_cfg = riscv_pmp_cfg # TODO
# self.mem_region = [] # TODO
# Self.amo_region = [] # TODO
self.mem_region = {
0: {'name': "region_0", 'size_in_bytes': 4096, 'xwr': 8},
1: {'name': "region_1", 'size_in_bytes': 4096 * 16, 'xwr': 8}
}
self.amo_region = {
0: {'name': "amo_0", 'size_in_bytes': 64, 'xwr': 8}
}
self.stack_len = 5000
# Self.s_mem_region = [] # TODO
self.s_mem_region = {
0: {'name': "s_region_0", 'size_in_bytes': 4096, 'xwr': 8},
1: {'name': "s_region_1", 'size_in_bytes': 4096, 'xwr': 8}
}
self.kernel_stack_len = 4000
self.kernel_program_instr_cnt = 400
# list of main implemented CSRs
self.invalid_priv_mode_csrs = []
self.num_of_sub_program = argv.num_of_sub_program
self.instr_cnt = argv.instr_cnt
self.num_of_tests = argv.num_of_tests
self.no_data_page = argv.no_data_page
self.no_branch_jump = argv.no_branch_jump
self.no_load_store = argv.no_load_store
self.no_csr_instr = argv.no_csr_instr
self.no_ebreak = argv.no_ebreak
self.no_dret = argv.no_dret
self.no_fence = argv.no_fence
self.no_wfi = argv.no_wfi
self.enable_unaligned_load_store = argv.enable_unaligned_load_store
self.illegal_instr_ratio = argv.illegal_instr_ratio
self.hint_instr_ratio = argv.hint_instr_ratio
self.num_of_harts = argv.num_of_harts
self.fix_sp = argv.fix_sp
self.use_push_data_section = argv.use_push_data_section
self.boot_mode_opts = argv.boot_mode_opts
self.num_of_sub_program = self.argv.num_of_sub_program
self.instr_cnt = self.argv.instr_cnt
self.num_of_tests = self.argv.num_of_tests
self.no_data_page = self.argv.no_data_page
self.no_branch_jump = self.argv.no_branch_jump
self.no_load_store = self.argv.no_load_store
self.no_csr_instr = self.argv.no_csr_instr
self.no_ebreak = self.argv.no_ebreak
self.no_dret = self.argv.no_dret
self.no_fence = self.argv.no_fence
self.no_wfi = self.argv.no_wfi
self.enable_unaligned_load_store = self.argv.enable_unaligned_load_store
self.illegal_instr_ratio = self.argv.illegal_instr_ratio
self.hint_instr_ratio = self.argv.hint_instr_ratio
self.num_of_harts = self.argv.num_of_harts
self.fix_sp = self.argv.fix_sp
self.use_push_data_section = self.argv.use_push_data_section
self.boot_mode_opts = self.argv.boot_mode
# self.isa = self.argv.isa
if(self.boot_mode_opts):
if self.boot_mode_opts:
logging.info("Got boot mode option - %0s", self.boot_mode_opts)
if(self.boot_mode_opts == "m"):
self.init_privileged_mode = privileged_mode_t.MACHINE_MODE.name
elif(self.boot_mode_opts == "s"):
self.init_privileged_mode = privileged_mode_t.SUPERVISOR_MODE.name
elif(self.boot_mode_opts == "u"):
self.init_privileged_mode = privileged_mode_t.USER_MODE.name
if self.boot_mode_opts == "m":
self.init_privileged_mode = privileged_mode_t.MACHINE_MODE
elif self.boot_mode_opts == "s":
self.init_privileged_mode = privileged_mode_t.SUPERVISOR_MODE
elif self.boot_mode_opts == "u":
self.init_privileged_mode = privileged_mode_t.USER_MODE
else:
logging.error("Illegal boot mode option - %0s", self.boot_mode_opts)
self.enable_page_table_exception = argv.enable_page_table_exception
self.no_directed_instr = argv.no_directed_instr
self.asm_test_suffix = argv.asm_test_suffix
self.enable_interrupt = argv.enable_interrupt
self.enable_nested_interrupt = argv.enable_nested_interrupt
self.enable_timer_irq = argv.enable_timer_irq
self.bare_program_mode = argv.bare_program_mode
self.enable_illegal_csr_instruction = argv.enable_illegal_csr_instruction
self.enable_access_invalid_csr_level = argv.enable_access_invalid_csr_level
self.enable_misaligned_instr = argv.enable_misaligned_instr
self.enable_dummy_csr_write = argv.enable_dummy_csr_write
self.randomize_csr = argv.randomize_csr
self.allow_sfence_exception = argv.allow_sfence_exception
self.no_delegation = argv.no_delegation
self.force_m_delegation = argv.force_m_delegation
self.force_s_delegation = argv.force_s_delegation
self.enable_page_table_exception = self.argv.enable_page_table_exception
self.no_directed_instr = self.argv.no_directed_instr
self.asm_test_suffix = self.argv.asm_test_suffix
self.enable_interrupt = self.argv.enable_interrupt
self.enable_nested_interrupt = self.argv.enable_nested_interrupt
self.enable_timer_irq = self.argv.enable_timer_irq
self.bare_program_mode = self.argv.bare_program_mode
self.enable_illegal_csr_instruction = self.argv.enable_illegal_csr_instruction
self.enable_access_invalid_csr_level = self.argv.enable_access_invalid_csr_level
self.enable_misaligned_instr = self.argv.enable_misaligned_instr
self.enable_dummy_csr_write = self.argv.enable_dummy_csr_write
self.randomize_csr = self.argv.randomize_csr
self.allow_sfence_exception = self.argv.allow_sfence_exception
self.no_delegation = self.argv.no_delegation
self.force_m_delegation = self.argv.force_m_delegation
self.force_s_delegation = self.argv.force_s_delegation
self.support_supervisor_mode = 0
self.disable_compressed_instr = argv.disable_compressed_instr
self.require_signature_addr = argv.require_signature_addr
self.disable_compressed_instr = self.argv.disable_compressed_instr
self.require_signature_addr = self.argv.require_signature_addr
if(self.require_signature_addr):
self.signature_addr = int(argv.signature_addr, 16)
if self.require_signature_addr:
self.signature_addr = int(self.argv.signature_addr, 16)
else:
self.signature_addr = 0xdeadbeef
self.gen_debug_section = argv.gen_debug_section
self.enable_ebreak_in_debug_rom = argv.enable_ebreak_in_debug_rom
self.set_dcsr_ebreak = argv.set_dcsr_ebreak
self.num_debug_sub_program = argv.num_debug_sub_program
self.enable_debug_single_step = argv.enable_debug_single_step
self.gen_debug_section = self.argv.gen_debug_section
self.enable_ebreak_in_debug_rom = self.argv.enable_ebreak_in_debug_rom
self.set_dcsr_ebreak = self.argv.set_dcsr_ebreak
self.num_debug_sub_program = self.argv.num_debug_sub_program
self.enable_debug_single_step = self.argv.enable_debug_single_step
self.single_step_iterations = 0
self.set_mstatus_tw = argv.set_mstatus_tw
self.set_mstatus_mprv = argv.set_mstatus_mprv
self.set_mstatus_tw = self.argv.set_mstatus_tw
self.set_mstatus_mprv = self.argv.set_mstatus_mprv
self.min_stack_len_per_program = 10 * (rcs.XLEN / 8)
self.max_stack_len_per_program = 16 * (rcs.XLEN / 8)
self.max_branch_step = 20
self.max_directed_instr_stream_seq = 20
self.reserved_regs = vsc.list_t(vsc.enum_t(riscv_reg_t))
self.enable_floating_point = argv.enable_floating_point
self.enable_vector_extension = argv.enable_vector_extension
self.enable_b_extension = argv.enable_b_extension
self.enable_bitmanip_groups = argv.enable_bitmanip_groups
self.enable_floating_point = self.argv.enable_floating_point
self.enable_vector_extension = self.argv.enable_vector_extension
self.enable_b_extension = self.argv.enable_b_extension
self.enable_bitmanip_groups = self.argv.enable_bitmanip_groups
self.dist_control_mode = 0
self.category_dist = {}
self.march_isa = argv.march_isa
self.march_isa = self.argv.march_isa
if(len(self.march_isa) != 0):
if len(self.march_isa) != 0:
rcs.supported_isa = self.march_isa
if(rcs.supported_isa != 'RV32C'):
if "RV32C" not in rcs.supported_isa:
self.disable_compressed_instr = 1
@vsc.constraint
def sp_tp_c(self):
if self.fix_sp:
self.sp == riscv_reg_t.SP
self.sp != self.tp
self.sp.not_inside(vsc.rangelist(riscv_reg_t.GP,
riscv_reg_t.RA, riscv_reg_t.ZERO))
self.tp.not_inside(vsc.rangelist(riscv_reg_t.GP,
riscv_reg_t.RA, riscv_reg_t.ZERO))
@vsc.constraint
def gpr_c(self):
self.gpr0.not_inside(vsc.rangelist(self.sp, self.tp, self.scratch_reg, self.pmp_reg,
@ -188,6 +206,25 @@ class riscv_instr_gen_config:
riscv_reg_t.ZERO, riscv_reg_t.RA, riscv_reg_t.GP))
vsc.unique(self.gpr0, self.gpr1, self.gpr2, self.gpr3)
@vsc.constraint
def ra_c(self):
self.ra != riscv_reg_t.SP
self.ra != riscv_reg_t.TP
self.ra != riscv_reg_t.ZERO
@vsc.constraint
def reserve_scratch_reg_c(self):
self.scratch_reg.not_inside(vsc.rangelist(riscv_reg_t.ZERO, self.sp,
self.tp, self.ra, riscv_reg_t.GP))
@vsc.constraint
def mtvec_c(self):
self.mtvec_mode.inside(vsc.rangelist(mtvec_mode_t.DIRECT, mtvec_mode_t.VECTORED))
if(self.mtvec_mode == mtvec_mode_t.DIRECT):
vsc.soft(self.tvec_alignment == 2)
else:
vsc.soft(self.tvec_alignment == (rcs.XLEN * 4) / 8)
def check_setting(self):
support_64b = 0
support_128b = 0
@ -257,13 +294,14 @@ class riscv_instr_gen_config:
# As it is being extended in post_randomize function.
self.gpr.clear()
for x in rcs.supported_privileged_mode:
if(x == "SUPERVISOR_MODE"):
if x == privileged_mode_t.SUPERVISOR_MODE:
self.support_supervisor_mode = 1
def get_non_reserved_gpr(self):
pass
def post_randomize(self):
self.reserved_regs = []
# Temporary fix for gpr_c constraint.
self.gpr.extend((self.gpr0, self.gpr1, self.gpr2, self.gpr3))
@ -273,10 +311,9 @@ class riscv_instr_gen_config:
self.min_stack_len_per_program = 2 * (rcs.XLEN / 8)
logging.info("min_stack_len_per_program value = %d"
% self.min_stack_len_per_program)
self.check_setting() # to check the setting is legal
self.check_setting() # check if the setting is legal
# TODO, Need to change the logic once the constraints are up.
if "USER_MODE" == self.init_privileged_mode:
if self.init_privileged_mode == privileged_mode_t.USER_MODE:
logging.info("mode=%s" % "USER_MODE")
self.no_wfi = 1
@ -288,13 +325,13 @@ class riscv_instr_gen_config:
# TODO Need to change the logic once the constraints are up.
for mode in self.init_privileged_mode:
if mode == "MACHINE_MODE":
if mode == privileged_mode_t.MACHINE_MODE:
continue
if mode == 'SUPERVISOR_MODE':
if mode == privileged_mode_t.SUPERVISOR_MODE:
invalid_lvl.append('M')
logging.info("supr_mode---")
logging.debug(invalid_lvl)
elif mode == 'USER_MODE':
elif mode == privileged_mode_t.USER_MODE:
invalid_lvl.append('S')
invalid_lvl.append('M')
logging.info("usr_mode---")
@ -315,123 +352,142 @@ class riscv_instr_gen_config:
# self.setup_instr_distribution() # TODO
self.get_invalid_priv_lvl_csr()
def parse_args(self):
parse = argparse.ArgumentParser()
parse.add_argument('--num_of_tests', help = 'num_of_tests', type = int, default = 1)
parse.add_argument('--enable_page_table_exception',
help = 'enable_page_table_exception', type = int, default = 0)
parse.add_argument('--enable_interrupt', help = 'enable_interrupt',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_nested_interrupt', help = 'enable_nested_interrupt',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_timer_irq', help = 'enable_timer_irq',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--num_of_sub_program', help = 'num_of_sub_program',
type = int, default = 5)
parse.add_argument('--instr_cnt', help = 'instr_cnt', type = int, default = 200)
parse.add_argument('--tvec_alignment', help = 'tvec_alignment', type = int, default = 2)
parse.add_argument('--no_ebreak', help = 'no_ebreak',
choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_dret', help = 'no_dret', choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_wfi', help = 'no_wfi', choices = [0, 1], type = int, default = 1)
def parse_args():
parse = argparse.ArgumentParser()
parse.add_argument('--num_of_tests', help = 'num_of_tests', type = int, default = 1)
parse.add_argument('--enable_page_table_exception',
help = 'enable_page_table_exception', type = int, default = 0)
parse.add_argument('--enable_interrupt', help = 'enable_interrupt',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_nested_interrupt', help = 'enable_nested_interrupt',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_timer_irq', help = 'enable_timer_irq',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--num_of_sub_program', help = 'num_of_sub_program', type = int, default = 5)
parse.add_argument('--instr_cnt', help = 'instr_cnt', type = int, default = 200)
parse.add_argument('--tvec_alignment', help = 'tvec_alignment', type = int, default = 2)
parse.add_argument('--no_ebreak', help = 'no_ebreak', choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_dret', help = 'no_dret', choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_wfi', help = 'no_wfi', choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_branch_jump', help = 'no_branch_jump',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_load_store', help = 'no_load_store',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_csr_instr', help = 'no_csr_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--fix_sp', help = 'fix_sp',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--use_push_data_section', help = 'use_push_data_section',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_illegal_csr_instruction',
help = 'enable_illegal_csr_instruction', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--enable_access_invalid_csr_level',
help = 'enable_access_invalid_csr_level', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--enable_misaligned_instr', help = 'enable_misaligned_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_dummy_csr_write', help = 'enable_dummy_csr_write',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--allow_sfence_exception', help = 'allow_sfence_exception',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_data_page', help = 'no_data_page',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_directed_instr', help = 'no_directed_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_fence', help = 'no_fence',
choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_delegation', help = 'no_delegation',
choices = [0, 1], type = int, default = 1)
parse.add_argument('--illegal_instr_ratio',
help = 'illegal_instr_ratio', type = int, default = 0)
parse.add_argument('--hint_instr_ratio', help = 'hint_instr_ratio', type = int, default = 0)
# TODO map it with rcs NUM_HARTS. After solving the cyclic issue for core setting.
# rcs is out of scope while using in the default value of num_of_harts
parse.add_argument('--num_of_harts', help = 'num_of_harts',
type = int, default = 1)
parse.add_argument('--enable_unaligned_load_store',
help = 'enable_unaligned_load_store', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--force_m_delegation', help = 'force_m_delegation',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--force_s_delegation', help = 'force_s_delegation',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--require_signature_addr', help = 'require_signature_addr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--signature_addr', help = 'signature_addr', default = 0xdeadbeef)
parse.add_argument('--disable_compressed_instr',
help = 'disable_compressed_instr', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--randomize_csr', help = 'randomize_csr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--gen_debug_section', help = 'gen_debug_section',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--bare_program_mode', help = 'bare_program_mode',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--num_debug_sub_program',
help = 'num_debug_sub_program', type = int, default = 0)
parse.add_argument('--enable_ebreak_in_debug_rom',
help = 'enable_ebreak_in_debug_rom', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--set_dcsr_ebreak', help = 'set_dcsr_ebreak',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_debug_single_step',
help = 'enable_debug_single_step', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--set_mstatus_tw', help = 'set_mstatus_tw',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--set_mstatus_mprv', help = 'set_mstatus_mprv',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_floating_point', help = 'enable_floating_point',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_vector_extension', help = 'enable_vector_extension',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_b_extension', help = 'enable_b_extension',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_bitmanip_groups', help = 'enable_bitmanip_groups',
default = ['ZBB', 'ZBS', 'ZBP', 'ZBE', 'ZBF',
'ZBC', 'ZBR', 'ZBM', 'ZBT', 'ZB_TMP'], nargs = '*')
parse.add_argument('--boot_mode', help = 'boot_mode', default = "")
parse.add_argument('--asm_test_suffix', help = 'asm_test_suffix', default = "")
parse.add_argument('--march_isa', help = 'march_isa', default = [],
choices = [i.name for i in riscv_instr_group_t], nargs = '*')
for i in range(self.max_directed_instr_stream_seq):
parse.add_argument('--directed_instr_{}'.format(i),
help = 'directed_instr_{}'.format(i), default = "")
parse.add_argument('--stream_name_{}'.format(i),
help = 'stream_name_{}'.format(i), default = "")
parse.add_argument('--stream_freq_{}'.format(i),
help = 'stream_freq_{}'.format(i), default = 4)
parse.add_argument('--start_idx', help='start index', type=int, default=0)
parse.add_argument('--asm_file_name', help='asm file name',
default="riscv_asm_test")
parse.add_argument('--log_file_name', help='log file name',
default="")
parse.add_argument('--target', help='target', default="rv32imc")
parse.add_argument("--enable_visualization", action="store_true", default=False,
help="Enabling coverage report visualization for pyflow")
parse.add_argument('--trace_csv', help='List of csv traces', default="")
args, unknown = parse.parse_known_args()
# TODO
'''
if ($value$plusargs("tvec_alignment=%0d", tvec_alignment)) begin
tvec_alignment.rand_mode(0);
end
parse.add_argument('--no_branch_jump', help = 'no_branch_jump',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_load_store', help = 'no_load_store',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_csr_instr', help = 'no_csr_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--fix_sp', help = 'fix_sp', choices = [0, 1], type = int, default = 0)
parse.add_argument('--use_push_data_section', help = 'use_push_data_section',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_illegal_csr_instruction',
help = 'enable_illegal_csr_instruction', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--enable_access_invalid_csr_level',
help = 'enable_access_invalid_csr_level', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--enable_misaligned_instr', help = 'enable_misaligned_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_dummy_csr_write', help = 'enable_dummy_csr_write',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--allow_sfence_exception', help = 'allow_sfence_exception',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_data_page', help = 'no_data_page',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_directed_instr', help = 'no_directed_instr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--no_fence', help = 'no_fence', choices = [0, 1], type = int, default = 1)
parse.add_argument('--no_delegation', help = 'no_delegation',
choices = [0, 1], type = int, default = 1)
parse.add_argument('--illegal_instr_ratio',
help = 'illegal_instr_ratio', type = int, default = 0)
parse.add_argument('--hint_instr_ratio', help = 'hint_instr_ratio', type = int, default = 0)
parse.add_argument('--num_of_harts', help = 'num_of_harts', type = int, default = rcs.NUM_HARTS)
parse.add_argument('--enable_unaligned_load_store',
help = 'enable_unaligned_load_store', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--force_m_delegation', help = 'force_m_delegation',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--force_s_delegation', help = 'force_s_delegation',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--require_signature_addr', help = 'require_signature_addr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--signature_addr', help = 'signature_addr', default = 0xdeadbeef)
parse.add_argument('--disable_compressed_instr',
help = 'disable_compressed_instr', choices = [0, 1], type = int, default = 0)
parse.add_argument('--randomize_csr', help = 'randomize_csr',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--gen_debug_section', help = 'gen_debug_section',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--bare_program_mode', help = 'bare_program_mode',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--num_debug_sub_program',
help = 'num_debug_sub_program', type = int, default = 0)
parse.add_argument('--enable_ebreak_in_debug_rom',
help = 'enable_ebreak_in_debug_rom', choices = [0, 1],
type = int, default = 0)
parse.add_argument('--set_dcsr_ebreak', help = 'set_dcsr_ebreak',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_debug_single_step',
help = 'enable_debug_single_step', choices = [0, 1], type = int, default = 0)
parse.add_argument('--set_mstatus_tw', help = 'set_mstatus_tw',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--set_mstatus_mprv', help = 'set_mstatus_mprv',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_floating_point', help = 'enable_floating_point',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_vector_extension', help = 'enable_vector_extension',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_b_extension', help = 'enable_b_extension',
choices = [0, 1], type = int, default = 0)
parse.add_argument('--enable_bitmanip_groups', help = 'enable_bitmanip_groups',
default = ['ZBB', 'ZBS', 'ZBP', 'ZBE', 'ZBF',
'ZBC', 'ZBR', 'ZBM', 'ZBT', 'ZB_TMP'], nargs = '*')
parse.add_argument('--boot_mode_opts', help = 'boot_mode_opts', default = "")
parse.add_argument('--asm_test_suffix', help = 'asm_test_suffix', default = "")
parse.add_argument('--march_isa', help = 'march_isa', default = [],
choices = [i.name for i in riscv_instr_group_t], nargs = '*')
parse.add_argument('--directed_instr_0', help = 'directed_instr_0',
default = "riscv_int_numeric_corner_stream,4")
parse.add_argument('--stream_name_opts', help = 'stream_name_0',
default = "riscv_load_store_rand_instr_stream")
parse.add_argument('--stream_freq_opts', help = 'stream_freq_0', default = 4)
# TODO
'''
if ($value$plusargs("tvec_alignment=%0d", tvec_alignment)) begin
tvec_alignment.rand_mode(0);
end
vector_cfg = riscv_vector_cfg::type_id::create("vector_cfg");
pmp_cfg = riscv_pmp_cfg::type_id::create("pmp_cfg");
pmp_cfg.rand_mode(pmp_cfg.pmp_randomize);
pmp_cfg.initialize(require_signature_addr);
setup_instr_distribution()
get_invalid_priv_lvl_csr();
'''
args = parse.parse_args()
vector_cfg = riscv_vector_cfg::type_id::create("vector_cfg");
pmp_cfg = riscv_pmp_cfg::type_id::create("pmp_cfg");
pmp_cfg.rand_mode(pmp_cfg.pmp_randomize);
pmp_cfg.initialize(require_signature_addr);
setup_instr_distribution();
get_invalid_priv_lvl_csr();
'''
args = parse.parse_args()
return args
return args
args = parse_args()
args_dict = vars(args)
cfg = riscv_instr_gen_config(args)
cfg = riscv_instr_gen_config()

View file

@ -15,7 +15,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
import logging
from enum import Enum, IntEnum, auto
from bitstring import BitArray
from pygen.pygen_src.target.rv32i import riscv_core_setting as rcs
from importlib import import_module
class mem_region_t:
@ -83,6 +83,7 @@ class riscv_instr_group_t(IntEnum):
RV64B = auto()
RV32X = auto()
RV64X = auto()
RVV = auto()
class riscv_instr_name_t(IntEnum):
@ -1081,6 +1082,8 @@ class rv32i_misc_instrs(IntEnum):
MRET = auto()
# Ignore RAW_HAZARD for store lsu hazard
class store_lsu_hazard_e(IntEnum):
NO_HAZARD = 0
WAR_HAZARD = auto()
@ -1432,21 +1435,24 @@ def add_functions_as_methods(function):
class riscv_instr_pkg:
def __init__(self):
self.MPRV_BIT_MASK = BitArray(uint=0x1 << 0x17, length=rcs.XLEN)
self.SUM_BIT_MASK = BitArray(uint=0x1 << 0x18, length=rcs.XLEN)
self.MPP_BIT_MASK = BitArray(uint=0x3 << 0x11, length=rcs.XLEN)
self.MAX_USED_VADDR_BITS = 30
self.IMM25_WIDTH = 25
self.IMM12_WIDTH = 12
self.INSTR_WIDTH = 32
self.DATA_WIDTH = 32
self.MAX_INSTR_STR_LEN = 11
self.LABEL_STR_LEN = 18
self.MAX_CALLSTACK_DEPTH = 20
self.MAX_SUB_PROGRAM_CNT = 20
self.MAX_CALL_PER_FUNC = 5
self.indent = self.LABEL_STR_LEN * " "
global rcs
from pygen_src.riscv_instr_gen_config import cfg
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
MPRV_BIT_MASK = BitArray(uint=0x1 << 0x17, length=rcs.XLEN)
SUM_BIT_MASK = BitArray(uint=0x1 << 0x18, length=rcs.XLEN)
MPP_BIT_MASK = BitArray(uint=0x3 << 0x11, length=rcs.XLEN)
MAX_USED_VADDR_BITS = 30
IMM25_WIDTH = 25
IMM12_WIDTH = 12
INSTR_WIDTH = 32
DATA_WIDTH = 32
MAX_INSTR_STR_LEN = 11
LABEL_STR_LEN = 18
MAX_CALLSTACK_DEPTH = 20
MAX_SUB_PROGRAM_CNT = 20
MAX_CALL_PER_FUNC = 5
indent = LABEL_STR_LEN * " "
def hart_prefix(self, hart=0):
if (rcs.NUM_HARTS <= 1):
@ -1470,14 +1476,73 @@ class riscv_instr_pkg:
if ((i % byte_per_group == 0) and (i != len(data) - 1) and (
i != 0)):
string = string + ", 0x"
string = string + f"{hex(data[i])}"
string = string + "{:02x}".format(data[i])
return string
def push_gpr_to_kernel_stack(self, status, scratch, mprv, sp, tp, instr):
pass
store_instr = ''
if(rcs.XLEN == 32):
store_instr = "sw"
else:
store_instr = "sd"
if (scratch.name in rcs.implemented_csr):
# Use kernal stack for handling exceptions. Save the user mode stack
# pointer to the scratch register
instr.append(pkg_ins.format_string(
"csrrw x{}, {}, x{}".format(sp, hex(scratch.value), sp)))
# Move TP to SP
instr.append(pkg_ins.format_string("add x{}, x{}, zero".format(sp, tp)))
# If MPRV is set and MPP is S/U mode, it means the address translation and
# memory protection for load/store instruction is the same as the mode indicated
# by MPP. In this case, we need to use the virtual address to access the kernel stack.
if((status.name == "MSTATUS") and (rcs.SATP_MODE != "BARE")):
# We temporarily use tp to check mstatus to avoid changing other GPR. The value
# of sp has been saved to xScratch and can be restored later.
if(mprv):
instr.append(pkg_ins.format_string(
"csrr x{}, 0x{} // MSTATUS".format(tp, status.value)))
instr.append(pkg_ins.format_string(
"srli x{}, x{}, 11".format(tp, tp))) # Move MPP to bit 0
instr.append(pkg_ins.format_string(
"andi x{}, x{}, 0x3".format(tp, tp))) # keep the MPP bits
# Check if MPP equals to M-mode('b11)
instr.append(pkg_ins.format_string("xori x{}, x{}, 0x3".format(tp, tp)))
# Use physical address for kernel SP
instr.append(pkg_ins.format_string("bnez x{}, 1f".format(tp)))
# Use virtual address for stack pointer
instr.append(pkg_ins.format_string(
"slli x{}, x{}, {}".format(sp, sp,
rcs.XLEN - riscv_instr_pkg.MAX_USED_VADDR_BITS)))
instr.append(pkg_ins.format_string(
"srli x{}, x{}, {}".format(sp, sp,
rcs.XLEN - riscv_instr_pkg.MAX_USED_VADDR_BITS)))
# Reserve space from kernel stack to save all 32 GPR except for x0
instr.append(pkg_ins.format_string(
"1: addi x{}, x{}, -{}".format(sp, sp, int(31 * (rcs.XLEN / 8)))))
# Push all GPRs to kernel stack
for i in range(1, 32):
instr.append(pkg_ins.format_string("{} x{}, {}(x{})".format(
store_instr, i, int(i * (rcs.XLEN / 8)), sp)))
def pop_gpr_from_kernel_stack(self, status, scratch, mprv, sp, tp, instr):
pass
load_instr = ''
if(rcs.XLEN == 32):
load_instr = "lw"
else:
load_instr = "ld"
# Pop user mode GPRs from kernel stack
for i in range(1, 32):
instr.append(pkg_ins.format_string("{} x{}, {}(x{})".format(
load_instr, i, int(i * (rcs.XLEN / 8)), sp)))
# Restore kernel stack pointer
instr.append(pkg_ins.format_string(
"addi x{}, x{}, {}".format(sp, sp, int(31 * (rcs.XLEN / 8)))))
if (scratch in rcs.implemented_csr):
# Move SP to TP
instr.append(pkg_ins.format_string("add x{}, x{}, zero".format(tp, sp)))
# Restore user mode stack pointer
instr.append(pkg_ins.format_string(
"csrrw x{}, 0x{}, x{}".format(sp, scratch.value, sp)))
pkg_ins = riscv_instr_pkg()

View file

@ -11,12 +11,19 @@ distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import re
import logging
import random
import sys
import vsc
from importlib import import_module
from collections import defaultdict
from pygen_src.riscv_instr_stream import riscv_rand_instr_stream
from pygen_src.riscv_instr_gen_config import cfg
from pygen_src.riscv_instr_pkg import pkg_ins, riscv_instr_category_t
from pygen_src.riscv_instr_pkg import (pkg_ins, riscv_instr_name_t, riscv_reg_t,
riscv_instr_category_t)
from pygen_src.riscv_directed_instr_lib import riscv_pop_stack_instr, riscv_push_stack_instr
rcs = import_module("pygen_src.target." + cfg.argv.target + ".riscv_core_setting")
class riscv_instr_sequence:
@ -33,6 +40,8 @@ class riscv_instr_sequence:
self.illegal_instr_pct = 0 # Percentage of illegal instructions
self.hint_instr_pct = 0 # Percentage of hint instructions
self.branch_idx = [None] * 30
self.instr_stack_enter = riscv_push_stack_instr()
self.instr_stack_exit = riscv_pop_stack_instr()
def gen_instr(self, is_main_program, no_branch = 1):
self.is_main_program = is_main_program
@ -49,9 +58,12 @@ class riscv_instr_sequence:
def gen_stack_enter_instr(self):
pass
# TODO
# Recover the saved GPR from the stack
# Advance the stack pointer(SP) to release the allocated stack space.
def gen_stack_exit_instr(self):
pass
self.instr_stack_exit.cfg = cfg
self.instr_stack_exit.gen_pop_stack_instr(self.program_stack_len,
self.instr_stack_enter.saved_regs)
'''
----------------------------------------------------------------------------------------------
@ -139,6 +151,7 @@ class riscv_instr_sequence:
logging.info("Processing branch instruction[%0d]:%0s # %0d -> %0d", j,
self.instr_stream.instr_list[j].convert2asm(),
self.instr_stream.instr_list[j].idx, branch_target_label)
logging.info("Branch", branch_target_label)
self.instr_stream.instr_list[j].imm_str = "{}f".format(branch_target_label)
self.instr_stream.instr_list[j].branch_assigned = 1
branch_target[branch_target_label] = 1
@ -177,11 +190,38 @@ class riscv_instr_sequence:
prefix = pkg_ins.format_string(string = " ", length = pkg_ins.LABEL_STR_LEN)
string = prefix + self.instr_stream.instr_list[i].convert2asm()
self.instr_string_list.append(string)
if(rcs.support_pmp and not re.search("main", self.label_name)):
self.instr_string_list.insert(0, ".align 2")
self.insert_illegal_hint_instr()
prefix = pkg_ins.format_string(str(i), pkg_ins.LABEL_STR_LEN)
if not self.is_main_program:
self.generate_return_routine(prefix)
# TODO
def generate_return_routine(self):
pass
def generate_return_routine(self, prefix):
string = ''
jump_instr = [riscv_instr_name_t.JALR]
rand_lsb = random.randrange(0, 1)
ra = vsc.enum_t(riscv_reg_t)
# TODO
# `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(ra,!(ra inside {cfg.reserved_regs}) ra != ZERO)
string = (prefix + pkg_ins.format_string("{}addi x{} x{} {}".format(ra.name,
cfg.ra.name, rand_lsb)))
self.instr_string_list.append(string)
if(not cfg.disable_compressed_instr):
jump_instr.append(riscv_instr_name_t.C_JR)
if(not (riscv_reg_t.RA in {cfg.reserved_regs})):
jump_instr.append(riscv_instr_name_t.C_JALR)
i = random.randrange(0, len(jump_instr) - 1)
if (jump_instr[i] == riscv_instr_name_t.C_JAL):
string = prefix + pkg_ins.format_string("{}c.jalr x{}".format(ra.name))
elif(jump_instr[i] == riscv_instr_name_t.C_JR):
string = prefix + pkg_ins.format_string("{}c.jr x{}".format(ra.name))
elif(jump_instr[i] == riscv_instr_name_t.JALR):
string = prefix + pkg_ins.format_string("{}c.jalr x{} x{} 0".format(ra.name, ra.name))
else:
logging.critical("Unsupported jump_instr: %0s" % (jump_instr[i]))
sys.exit(1)
self.instr_string_list.append(string)
# TODO
def insert_illegal_hint_instr(self):

View file

@ -15,8 +15,8 @@ import logging
import sys
import vsc
from pygen_src.riscv_instr_pkg import riscv_instr_name_t,\
riscv_instr_category_t, riscv_reg_t
from pygen_src.isa.riscv_instr import riscv_instr, riscv_instr_ins
riscv_instr_category_t, riscv_instr_format_t, riscv_reg_t
from pygen_src.isa.riscv_instr import riscv_instr
from pygen_src.riscv_instr_gen_config import cfg
@ -34,10 +34,10 @@ class riscv_instr_stream:
self.instr_cnt = 0
self.label = ""
# User can specify a small group of available registers to generate various hazard condition
self.avail_regs = vsc.randsz_list_t(vsc.enum_t(riscv_reg_t))
self.avail_regs = vsc.rand_list_t(vsc.enum_t(riscv_reg_t), sz = 10)
# Some additional reserved registers that should not be used as rd register
# by this instruction stream
self.reserved_rd = []
self.reserved_rd = vsc.list_t(vsc.enum_t(riscv_reg_t))
self.hart = 0
def initialize_instr_list(self, instr_cnt):
@ -106,16 +106,16 @@ class riscv_instr_stream:
new_instr[0].label = self.instr_list[idx].label
new_instr[0].has_label = self.instr_list[idx].has_label
if idx == 0:
self.instr_list = new_instr + self.instr_list[idx + 1:current_instr_cnt - 1]
else:
self.instr_list = self.instr_list[0:idx - 1] + new_instr + \
self.instr_list[idx + 1:current_instr_cnt - 1]
else:
if idx == 0:
self.instr_list = new_instr + self.instr_list[idx:current_instr_cnt - 1]
self.instr_list = new_instr + self.instr_list[idx + 1:current_instr_cnt]
else:
self.instr_list = self.instr_list[0:idx] + new_instr + \
self.instr_list[idx:current_instr_cnt - 1]
self.instr_list[idx + 1:current_instr_cnt]
else:
if idx == 0:
self.instr_list = new_instr + self.instr_list[idx:current_instr_cnt]
else:
self.instr_list = self.instr_list[0:idx] + new_instr + \
self.instr_list[idx:current_instr_cnt]
def mix_instr_stream(self, new_instr, contained = 0):
"""
@ -170,15 +170,15 @@ class riscv_rand_instr_stream(riscv_instr_stream):
self.instr_list.append(None)
def setup_allowed_instr(self, no_branch = 0, no_load_store = 1):
self.allowed_instr = riscv_instr_ins.basic_instr
self.allowed_instr = riscv_instr.basic_instr
if no_branch == 0:
self.allowed_instr.extend(
riscv_instr_ins.instr_category[riscv_instr_category_t.BRANCH.name])
riscv_instr.instr_category[riscv_instr_category_t.BRANCH.name])
if no_load_store == 0:
self.allowed_instr.extend(
riscv_instr_ins.instr_category[riscv_instr_category_t.LOAD.name])
riscv_instr.instr_category[riscv_instr_category_t.LOAD.name])
self.allowed_instr.extend(
riscv_instr_ins.instr_category[riscv_instr_category_t.STORE.name])
riscv_instr.instr_category[riscv_instr_category_t.STORE.name])
self.setup_instruction_dist(no_branch, no_load_store)
# TODO
@ -215,20 +215,39 @@ class riscv_rand_instr_stream(riscv_instr_stream):
exclude_instr.append(riscv_instr_name_t.C_ADDI16SP.name)
exclude_instr.append(riscv_instr_name_t.C_LWSP.name)
exclude_instr.append(riscv_instr_name_t.C_LDSP.name)
if is_in_debug and (not cfg.enable_ebreak_in_debug_rom):
exclude_instr.append(riscv_instr_name_t.EBREAK.name)
exclude_instr.append(riscv_instr_name_t.C_EBREAK.name)
instr = riscv_instr_ins.get_rand_instr(
# Post-process the allowed_instr and exclude_instr lists to handle
# adding ebreak instructions into the debug ROM.
if is_in_debug:
if (cfg.no_ebreak and cfg.enable_ebreak_in_debug_rom):
self.allowed_instr.extend([riscv_instr_name_t.EBREAK.name,
riscv_instr_name_t.C_EBREAK.name])
elif (not cfg.no_ebreak and not cfg.enable_ebreak_in_debug_rom):
exclude_instr.extend([riscv_instr_name_t.EBREAK.name,
riscv_instr_name_t.C_EBREAK.name])
instr = riscv_instr.get_rand_instr(
include_instr = self.allowed_instr, exclude_instr = exclude_instr)
instr = self.randomize_gpr(instr)
return instr
def randomize_gpr(self, instr):
# TODO
"""
PyVSC library doesn't support inline randomization for list of enum types.
The randomization is done directly here.
it will be updated once randomization for list of enum types supports in PyVSC.
"""
instr.randomize()
with instr.randomize_with() as it:
if self.avail_regs.size > 0:
if instr.has_rs1:
instr.rs1.inside(vsc.rangelist(self.avail_regs))
if instr.has_rs2:
instr.rs2.inside(vsc.rangelist(self.avail_regs))
if instr.has_rd:
instr.rd.inside(vsc.rangelist(self.avail_regs))
with vsc.foreach(self.reserved_rd, idx = True) as i:
if instr.has_rd == 1:
instr.rd != self.reserved_rd[i]
if instr.format == riscv_instr_format_t.CB_FORMAT:
instr.rs1 != self.reserved_rd[i]
with vsc.foreach(cfg.reserved_regs, idx = True) as i:
with vsc.if_then(instr.has_rd == 1):
instr.rd != cfg.reserved_regs[i]
with vsc.if_then(instr.format == riscv_instr_format_t.CB_FORMAT):
instr.rs1 != cfg.reserved_regs[i]
# TODO: Add constraint for CSR, floating point register
return instr

View file

@ -13,14 +13,22 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import sys
import logging
import pandas as pd
from tabulate import tabulate
from pygen_src.isa import rv32i_instr
from pygen_src.isa import rv32m_instr
from pygen_src.isa import rv32c_instr
from pygen_src.riscv_instr_gen_config import cfg
from pygen_src.riscv_directed_instr_lib import (riscv_directed_instr_stream,
riscv_int_numeric_corner_stream)
riscv_int_numeric_corner_stream,
riscv_jal_instr)
def factory(obj_of):
objs = {
"riscv_directed_instr_stream": riscv_directed_instr_stream,
"riscv_int_numeric_corner_stream": riscv_int_numeric_corner_stream
"riscv_int_numeric_corner_stream": riscv_int_numeric_corner_stream,
"riscv_jal_instr": riscv_jal_instr
}
try:
@ -28,3 +36,157 @@ def factory(obj_of):
except KeyError:
logging.critical("Cannot Create object of %s", obj_of)
sys.exit(1)
def get_object(instr):
try:
instr_inst = eval("rv32i_instr.riscv_" + instr.name + "_instr()")
except Exception:
try:
instr_inst = eval("rv32m_instr.riscv_" + instr.name + "_instr()")
except Exception:
try:
instr_inst = eval("rv32c_instr.riscv_" + instr.name + "_instr()")
except Exception:
logging.critical("Failed to create instr: %0s", instr.name)
sys.exit(1)
return instr_inst
def gen_config_table():
data = []
data.append(['main_program_instr_cnt', type(cfg.main_program_instr_cnt),
sys.getsizeof(cfg.main_program_instr_cnt), cfg.main_program_instr_cnt])
data.append(['sub_program_instr_cnt', type(cfg.sub_program_instr_cnt),
sys.getsizeof(cfg.sub_program_instr_cnt), cfg.sub_program_instr_cnt])
data.append(['debug_program_instr_cnt', type(cfg.debug_program_instr_cnt),
sys.getsizeof(cfg.debug_program_instr_cnt), cfg.debug_program_instr_cnt])
data.append(['debug_sub_program_instr_cnt', type(cfg.debug_sub_program_instr_cnt),
sys.getsizeof(cfg.debug_sub_program_instr_cnt),
cfg.debug_sub_program_instr_cnt])
data.append(['max_directed_instr_stream_seq', type(cfg.max_directed_instr_stream_seq),
sys.getsizeof(cfg.max_directed_instr_stream_seq),
cfg.max_directed_instr_stream_seq])
data.append(['data_page_pattern', type(cfg.data_page_pattern),
sys.getsizeof(cfg.data_page_pattern), cfg.data_page_pattern])
data.append(['init_privileged_mode', type(cfg.init_privileged_mode),
sys.getsizeof(cfg.init_privileged_mode), cfg.init_privileged_mode])
data.append(['scratch_reg', type(cfg.scratch_reg),
sys.getsizeof(cfg.scratch_reg), cfg.scratch_reg])
data.append(['pmp_reg', type(cfg.pmp_reg), sys.getsizeof(cfg.pmp_reg), cfg.pmp_reg])
data.append(['reserved_regs', type(cfg.reserved_regs),
sys.getsizeof(cfg.reserved_regs), cfg.reserved_regs])
data.append(['sp', type(cfg.sp), sys.getsizeof(cfg.sp), cfg.sp])
data.append(['tp', type(cfg.tp), sys.getsizeof(cfg.tp), cfg.tp])
data.append(['ra', type(cfg.ra), sys.getsizeof(cfg.ra), cfg.ra])
data.append(['check_misa_init_val', type(cfg.check_misa_init_val),
sys.getsizeof(cfg.check_misa_init_val), cfg.check_misa_init_val])
data.append(['check_xstatus', type(cfg.check_xstatus),
sys.getsizeof(cfg.check_xstatus), cfg.check_xstatus])
data.append(['virtual_addr_translation_on', type(cfg.virtual_addr_translation_on),
sys.getsizeof(cfg.virtual_addr_translation_on),
cfg.virtual_addr_translation_on])
data.append(['kernel_stack_len', type(cfg.kernel_stack_len),
sys.getsizeof(cfg.kernel_stack_len), cfg.kernel_stack_len])
data.append(['kernel_program_instr_cnt', type(cfg.kernel_program_instr_cnt),
sys.getsizeof(cfg.kernel_program_instr_cnt), cfg.kernel_program_instr_cnt])
data.append(['num_of_sub_program', type(cfg.num_of_sub_program),
sys.getsizeof(cfg.num_of_sub_program), cfg.num_of_sub_program])
data.append(['instr_cnt', type(cfg.instr_cnt), sys.getsizeof(cfg.instr_cnt), cfg.instr_cnt])
data.append(['num_of_tests', type(cfg.num_of_tests),
sys.getsizeof(cfg.num_of_tests), cfg.num_of_tests])
data.append(['no_data_page', type(cfg.no_data_page),
sys.getsizeof(cfg.no_data_page), cfg.no_data_page])
data.append(['no_branch_jump', type(cfg.no_branch_jump),
sys.getsizeof(cfg.no_branch_jump), cfg.no_branch_jump])
data.append(['no_load_store', type(cfg.no_load_store),
sys.getsizeof(cfg.no_load_store), cfg.no_load_store])
data.append(['no_csr_instr', type(cfg.no_csr_instr),
sys.getsizeof(cfg.no_csr_instr), cfg.no_csr_instr])
data.append(['no_ebreak', type(cfg.no_ebreak), sys.getsizeof(cfg.no_ebreak), cfg.no_ebreak])
data.append(['no_dret', type(cfg.no_dret), sys.getsizeof(cfg.no_dret), cfg.no_dret])
data.append(['no_fence', type(cfg.no_fence), sys.getsizeof(cfg.no_fence), cfg.no_fence])
data.append(['no_wfi', type(cfg.no_wfi), sys.getsizeof(cfg.no_wfi), cfg.no_wfi])
data.append(['enable_unaligned_load_store', type(cfg.enable_unaligned_load_store),
sys.getsizeof(cfg.enable_unaligned_load_store),
cfg.enable_unaligned_load_store])
data.append(['illegal_instr_ratio', type(cfg.illegal_instr_ratio),
sys.getsizeof(cfg.illegal_instr_ratio), cfg.illegal_instr_ratio])
data.append(['hint_instr_ratio', type(cfg.hint_instr_ratio),
sys.getsizeof(cfg.hint_instr_ratio), cfg.hint_instr_ratio])
data.append(['num_of_harts', type(cfg.num_of_harts),
sys.getsizeof(cfg.num_of_harts), cfg.num_of_harts])
data.append(['fix_sp', type(cfg.fix_sp), sys.getsizeof(cfg.fix_sp), cfg.fix_sp])
data.append(['use_push_data_section', type(cfg.use_push_data_section),
sys.getsizeof(cfg.use_push_data_section), cfg.use_push_data_section])
data.append(['boot_mode_opts', type(cfg.boot_mode_opts),
sys.getsizeof(cfg.boot_mode_opts), cfg.boot_mode_opts])
data.append(['enable_page_table_exception', type(cfg.enable_page_table_exception),
sys.getsizeof(cfg.enable_page_table_exception),
cfg.enable_page_table_exception])
data.append(['no_directed_instr', type(cfg.no_directed_instr),
sys.getsizeof(cfg.no_directed_instr), cfg.no_directed_instr])
data.append(['asm_test_suffix', type(cfg.asm_test_suffix),
sys.getsizeof(cfg.asm_test_suffix), cfg.asm_test_suffix])
data.append(['enable_interrupt', type(cfg.enable_interrupt),
sys.getsizeof(cfg.enable_interrupt), cfg.enable_interrupt])
data.append(['enable_nested_interrupt', type(cfg.enable_nested_interrupt),
sys.getsizeof(cfg.enable_nested_interrupt), cfg.enable_nested_interrupt])
data.append(['enable_timer_irq', type(cfg.enable_timer_irq),
sys.getsizeof(cfg.enable_timer_irq), cfg.enable_timer_irq])
data.append(['bare_program_mode', type(cfg.bare_program_mode),
sys.getsizeof(cfg.bare_program_mode), cfg.bare_program_mode])
data.append(['enable_illegal_csr_instruction', type(cfg.enable_illegal_csr_instruction),
sys.getsizeof(cfg.enable_illegal_csr_instruction),
cfg.enable_illegal_csr_instruction])
data.append(['enable_access_invalid_csr_level', type(cfg.enable_access_invalid_csr_level),
sys.getsizeof(cfg.enable_access_invalid_csr_level),
cfg.enable_access_invalid_csr_level])
data.append(['enable_misaligned_instr', type(cfg.enable_misaligned_instr),
sys.getsizeof(cfg.enable_misaligned_instr), cfg.enable_misaligned_instr])
data.append(['enable_dummy_csr_write', type(cfg.enable_dummy_csr_write),
sys.getsizeof(cfg.enable_dummy_csr_write), cfg.enable_dummy_csr_write])
data.append(['randomize_csr', type(cfg.randomize_csr),
sys.getsizeof(cfg.randomize_csr), cfg.randomize_csr])
data.append(['allow_sfence_exception', type(cfg.allow_sfence_exception),
sys.getsizeof(cfg.allow_sfence_exception), cfg.allow_sfence_exception])
data.append(['no_delegation', type(cfg.no_delegation),
sys.getsizeof(cfg.no_delegation), cfg.no_delegation])
data.append(['force_m_delegation', type(cfg.force_m_delegation),
sys.getsizeof(cfg.force_m_delegation), cfg.force_m_delegation])
data.append(['force_s_delegation', type(cfg.force_s_delegation),
sys.getsizeof(cfg.force_s_delegation), cfg.force_s_delegation])
data.append(['support_supervisor_mode', type(cfg.support_supervisor_mode),
sys.getsizeof(cfg.support_supervisor_mode), cfg.support_supervisor_mode])
data.append(['disable_compressed_instr', type(cfg.disable_compressed_instr),
sys.getsizeof(cfg.disable_compressed_instr), cfg.disable_compressed_instr])
data.append(['require_signature_addr', type(cfg.require_signature_addr),
sys.getsizeof(cfg.require_signature_addr), cfg.require_signature_addr])
data.append(['signature_addr', type(cfg.signature_addr),
sys.getsizeof(cfg.signature_addr), cfg.signature_addr])
data.append(['gen_debug_section', type(cfg.gen_debug_section),
sys.getsizeof(cfg.gen_debug_section), cfg.force_s_delegation])
data.append(['enable_ebreak_in_debug_rom', type(cfg.enable_ebreak_in_debug_rom),
sys.getsizeof(cfg.enable_ebreak_in_debug_rom), cfg.enable_ebreak_in_debug_rom])
data.append(['set_dcsr_ebreak', type(cfg.set_dcsr_ebreak),
sys.getsizeof(cfg.set_dcsr_ebreak), cfg.set_dcsr_ebreak])
data.append(['num_debug_sub_program', type(cfg.num_debug_sub_program),
sys.getsizeof(cfg.num_debug_sub_program), cfg.num_debug_sub_program])
data.append(['enable_debug_single_step', type(cfg.enable_debug_single_step),
sys.getsizeof(cfg.enable_debug_single_step), cfg.enable_debug_single_step])
data.append(['single_step_iterations', type(cfg.single_step_iterations),
sys.getsizeof(cfg.single_step_iterations), cfg.single_step_iterations])
data.append(['set_mstatus_tw', type(cfg.set_mstatus_tw),
sys.getsizeof(cfg.set_mstatus_tw), cfg.set_mstatus_tw])
data.append(['set_mstatus_mprv', type(cfg.set_mstatus_mprv),
sys.getsizeof(cfg.set_mstatus_mprv), cfg.set_mstatus_mprv])
data.append(['enable_floating_point', type(cfg.enable_floating_point),
sys.getsizeof(cfg.enable_floating_point), cfg.enable_floating_point])
data.append(['enable_vector_extension', type(cfg.enable_vector_extension),
sys.getsizeof(cfg.enable_vector_extension), cfg.enable_vector_extension])
data.append(['enable_b_extension', type(cfg.enable_b_extension),
sys.getsizeof(cfg.enable_b_extension), cfg.enable_b_extension])
df = pd.DataFrame(data, columns=['Name', 'Type', 'Size', 'Value'])
df['Value'] = df['Value'].apply(str)
logging.info('\n' + tabulate(df, headers='keys', tablefmt='psql'))

View file

@ -0,0 +1,21 @@
# riscOVPsim configuration file converted from YAML
--variant RV32I
--override riscvOVPsim/cpu/misa_MXL=1
--override riscvOVPsim/cpu/misa_MXL_mask=0x0 # 0
--override riscvOVPsim/cpu/misa_Extensions_mask=0x0 # 0
--override riscvOVPsim/cpu/unaligned=T
--override riscvOVPsim/cpu/mtvec_mask=0x0 # 0
--override riscvOVPsim/cpu/user_version=2.3
--override riscvOVPsim/cpu/priv_version=1.11
--override riscvOVPsim/cpu/mvendorid=0
--override riscvOVPsim/cpu/marchid=0
--override riscvOVPsim/cpu/mimpid=0
--override riscvOVPsim/cpu/mhartid=0
--override riscvOVPsim/cpu/cycle_undefined=F
--override riscvOVPsim/cpu/instret_undefined=F
--override riscvOVPsim/cpu/time_undefined=T
--override riscvOVPsim/cpu/reset_address=0x80000000
--override riscvOVPsim/cpu/simulateexceptions=T
--override riscvOVPsim/cpu/defaultsemihost=F
--override riscvOVPsim/cpu/wfi_is_nop=T
--exitonsymbol _exit

View file

@ -29,3 +29,8 @@ support_pmp = 0
unsupported_instr = []
support_umode_trap = 0
# GPR Setting
NUM_FLOAT_GPR = 32
NUM_GPR = 32
NUM_VEC_GPR = 32

View file

@ -0,0 +1,22 @@
# riscOVPsim configuration file converted from YAML
--variant RV32I
--override riscvOVPsim/cpu/add_Extensions=MC
--override riscvOVPsim/cpu/misa_MXL=1
--override riscvOVPsim/cpu/misa_MXL_mask=0x0 # 0
--override riscvOVPsim/cpu/misa_Extensions_mask=0x0 # 0
--override riscvOVPsim/cpu/unaligned=T
--override riscvOVPsim/cpu/mtvec_mask=0x0 # 0
--override riscvOVPsim/cpu/user_version=2.3
--override riscvOVPsim/cpu/priv_version=1.11
--override riscvOVPsim/cpu/mvendorid=0
--override riscvOVPsim/cpu/marchid=0
--override riscvOVPsim/cpu/mimpid=0
--override riscvOVPsim/cpu/mhartid=0
--override riscvOVPsim/cpu/cycle_undefined=F
--override riscvOVPsim/cpu/instret_undefined=F
--override riscvOVPsim/cpu/time_undefined=T
--override riscvOVPsim/cpu/reset_address=0x80000000
--override riscvOVPsim/cpu/simulateexceptions=T
--override riscvOVPsim/cpu/defaultsemihost=F
--override riscvOVPsim/cpu/wfi_is_nop=T
--exitonsymbol _exit

View file

@ -0,0 +1,62 @@
"""
Copyright 2020 Google LLC
Copyright 2020 PerfectVIPs Inc.
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.
"""
XLEN = 32
implemented_csr = ['MVENDORID', 'MARCHID', 'MIMPID', 'MHARTID', 'MSTATUS', 'MISA', 'MIE',
'MTVEC', 'MCOUNTEREN', 'MSCRATCH', 'MEPC', 'MCAUSE', 'MTVAL', 'MIP']
SATP_MODE = 'BARE'
supported_isa = ['RV32I', 'RV32M', 'RV32C']
supported_privileged_mode = ['MACHINE_MODE']
supported_interrupt_mode = ['DIRECT', 'VECTORED']
max_interrupt_vector_num = 16
support_debug_mode = 0
NUM_HARTS = 1
support_pmp = 0
unsupported_instr = []
support_umode_trap = 0
support_sfence = 0
support_unaligned_load_store = 1
# GPR Setting
NUM_FLOAT_GPR = 32
NUM_GPR = 32
NUM_VEC_GPR = 32
VECTOR_EXTENSION_ENABLE = 0
VLEN = 512
ELEN = 32
SELEN = 0
MAX_MUL = 8
implemented_interrupt = ['M_SOFTWARE_INTR', 'M_TIMER_INTR', 'M_EXTERNAL_INTR']
implemented_exception = ['INSTRUCTION_ACCESS_FAULT', 'ILLEGAL_INSTRUCTION',
'BREAKPOINT', 'LOAD_ADDRESS_MISALIGNED', 'LOAD_ACCESS_FAULT',
'ECALL_MMODE']

View file

@ -12,22 +12,40 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
"""
import sys
sys.path.append("../../")
sys.path.append("pygen/")
from pygen_src.riscv_instr_pkg import *
from pygen_src.riscv_instr_gen_config import cfg # NOQA
from pygen_src.isa.rv32i_instr import * # NOQA
from pygen_src.isa.riscv_instr import riscv_instr_ins # NOQA
if cfg.argv.target == "rv32i":
from pygen_src.isa.rv32i_instr import * # NOQA
if cfg.argv.target == "rv32imc":
from pygen_src.isa.rv32i_instr import * # NOQA
from pygen_src.isa.rv32m_instr import * # NOQA
from pygen_src.isa.rv32c_instr import * # NOQA
from pygen_src.isa.riscv_instr import riscv_instr # NOQA
from pygen_src.riscv_asm_program_gen import riscv_asm_program_gen # NOQA
from pygen_src.riscv_utils import gen_config_table
class riscv_instr_base_test:
def __init__(self):
pass
self.start_idx = cfg.argv.start_idx
self.asm_file_name = cfg.argv.asm_file_name
for _ in range(cfg.num_of_tests):
cfg.randomize()
asm = riscv_asm_program_gen()
riscv_instr_ins.create_instr_list(cfg)
test_name = "riscv_arithmetic_basic_test_{}.S".format(_)
asm.get_directed_instr_stream()
asm.gen_program()
asm.gen_test_file(test_name)
def run_phase(self):
for _ in range(cfg.num_of_tests):
cfg.randomize()
gen_config_table()
asm = riscv_asm_program_gen()
riscv_instr.create_instr_list(cfg)
if cfg.asm_test_suffix != "":
self.asm_file_name = "{}.{}".format(self.asm_file_name,
cfg.asm_test_suffix)
test_name = "{}_{}.S".format(self.asm_file_name,
_ + self.start_idx)
asm.get_directed_instr_stream()
asm.gen_program()
asm.gen_test_file(test_name)
riscv_instr_base_test = riscv_instr_base_test()
riscv_instr_base_test.run_phase()

View file

@ -17,32 +17,33 @@ import sys
import vsc
import csv
from tabulate import *
from pygen.pygen_src.isa.riscv_cov_instr import riscv_cov_instr
from pygen.pygen_src.riscv_instr_cover_group import *
from pygen.pygen_src.riscv_instr_pkg import *
logging.basicConfig(filename='logging_two_files_new.log', filemode='w',
format="%(filename)s %(lineno)s %(levelname)s %(message)s",
level=logging.ERROR)
sys.path.append("pygen/")
from pygen_src.riscv_instr_pkg import *
from pygen_src.isa.riscv_cov_instr import riscv_cov_instr
from pygen_src.riscv_instr_cover_group import *
class riscv_instr_cov_test:
""" Main class for applying the functional coverage test """
def __init__(self, argv):
def __init__(self):
self.instr_cg = riscv_instr_cover_group()
self.trace = {}
self.csv_trace = argv
self.csv_trace = []
self.entry_cnt, self.total_entry_cnt, self.skipped_cnt, \
self.unexpected_illegal_instr_cnt = 0, 0, 0, 0
logging.basicConfig(filename='{}'.format(cfg.argv.log_file_name),
filemode='w',
format="%(filename)s %(lineno)s %(levelname)s %(message)s",
level=logging.DEBUG)
def run_phase(self):
self.csv_trace = cfg.argv.trace_csv.split(",")
if not self.csv_trace:
sys.exit("No CSV file found!")
logging.info("{} CSV trace files to be "
"processed...\n".format(len(self.csv_trace)))
expect_illegal_instr = False
# Assuming we get list of csv files pathname from cov.py in argv
for csv_file in self.csv_trace:
with open("{}".format(csv_file)) as trace_file:
self.entry_cnt = 0
@ -102,10 +103,10 @@ class riscv_instr_cov_test:
self.unexpected_illegal_instr_cnt))
self.get_coverage_report()
@staticmethod
def get_coverage_report():
def get_coverage_report(self):
model = vsc.get_coverage_report_model()
file = open('CoverageReport.txt', 'w')
cov_dir = cfg.argv.log_file_name.split("/")[0]
file = open('{}/CoverageReport.txt'.format(cov_dir), 'w')
file.write("Groups Coverage Summary\n")
file.write("Total groups in report: {}\n".format(
len(model.covergroups)))
@ -116,8 +117,9 @@ class riscv_instr_cov_test:
file.write(tabulate(table, headers, tablefmt="grid",
numalign="center", stralign="center"))
file.close()
# Write in xml format to be read by pyucis-viewer (visualization)
vsc.write_coverage_db("cov_db.xml")
# If enabled, write in xml format to be read by pyucis-viewer (visualization)
if cfg.argv.enable_visualization:
vsc.write_coverage_db("{}/cov_db.xml".format(cov_dir))
def post_process_trace(self):
pass
@ -200,10 +202,7 @@ class riscv_instr_cov_test:
return instruction
def main(argv):
cov_test = riscv_instr_cov_test(argv)
cov_test.run_phase()
cov_test = riscv_instr_cov_test()
cov_test.run_phase()
if __name__ == "__main__":
main(sys.argv)

View file

@ -9,3 +9,4 @@ rst2pdf
flake8
pyvsc
tabulate
pandas

File diff suppressed because it is too large Load diff

View file

@ -21,7 +21,6 @@ in the CSR at this point, allowing for the test to self-check in order to
determine success or failure.
"""
"""
To install the bitstring library:
1) sudo apt-get install python3-bitstring OR
@ -35,325 +34,361 @@ import copy
from lib import *
try:
from bitstring import BitArray as bitarray
from bitstring import BitArray as bitarray
except ImportError as e:
logging.error("Please install bitstring package: sudo apt-get install python3-bitstring")
sys.exit(RET_FAIL)
logging.error(
"Please install bitstring package: sudo apt-get install python3-bitstring")
sys.exit(RET_FAIL)
"""
Defines the test's success/failure values, one of which will be written to
the chosen signature address to indicate the test's result.
"""
TEST_RESULT = 1
TEST_PASS = 0
TEST_FAIL = 1
TEST_PASS = 0
TEST_FAIL = 1
def get_csr_map(csr_file, xlen):
"""
Parses the YAML file containing CSR descriptions.
"""
Parses the YAML file containing CSR descriptions.
Args:
csr_file: The CSR YAML file.
xlen: The current RISC-V ISA bit length.
Args:
csr_file: The CSR YAML file.
xlen: The current RISC-V ISA bit length.
Returns:
A dictionary contining mappings for each CSR, of the form:
{ csr_name : [csr_address, csr_val_bitarray, csr_write_mask_bitarray, csr_read_mask_bitarray] }
"""
rv_string = "rv{}".format(str(xlen))
csrs = {}
with open(csr_file, "r") as c:
csr_description = yaml.safe_load(c)
for csr_dict in csr_description:
csr_name = csr_dict.get("csr")
csr_address = csr_dict.get("address")
assert(rv_string in csr_dict), "The {} CSR must be configured for rv{}".format(csr_name, str(rv))
csr_value = bitarray(uintbe=0, length=xlen)
csr_write_mask = []
csr_read_mask = bitarray(uintbe=0, length=xlen)
csr_field_list = csr_dict.get(rv_string)
for csr_field_detail_dict in csr_field_list:
field_type = csr_field_detail_dict.get("type")
field_val = csr_field_detail_dict.get("reset_val")
field_msb = csr_field_detail_dict.get("msb")
field_lsb = csr_field_detail_dict.get("lsb")
field_size = field_msb - field_lsb + 1
if field_type != "WPRI":
val_bitarray = bitarray(uint=field_val, length=field_size)
mask_bitarray = bitarray(uint=1, length=1) * field_size
start_pos = xlen - 1 - field_msb
end_pos = xlen - 1 - field_lsb
csr_read_mask.overwrite(mask_bitarray, xlen - 1 - field_msb)
csr_value.overwrite(val_bitarray, xlen - 1 - field_msb)
access = True if field_type == "R" else False
csr_write_mask.append([mask_bitarray, (start_pos, end_pos), access])
csrs.update({csr_name : [csr_address, csr_value, csr_write_mask, csr_read_mask]})
return csrs
Returns:
A dictionary contining mappings for each CSR, of the form:
{ csr_name : [csr_address, csr_val_bitarray, csr_write_mask_bitarray, csr_read_mask_bitarray] }
"""
rv_string = "rv{}".format(str(xlen))
csrs = {}
with open(csr_file, "r") as c:
csr_description = yaml.safe_load(c)
for csr_dict in csr_description:
csr_name = csr_dict.get("csr")
csr_address = csr_dict.get("address")
assert (rv_string in csr_dict), "The {} CSR must be configured for rv{}".format(
csr_name, str(rv))
csr_value = bitarray(uintbe=0, length=xlen)
csr_write_mask = []
csr_read_mask = bitarray(uintbe=0, length=xlen)
csr_field_list = csr_dict.get(rv_string)
for csr_field_detail_dict in csr_field_list:
field_type = csr_field_detail_dict.get("type")
field_val = csr_field_detail_dict.get("reset_val")
field_msb = csr_field_detail_dict.get("msb")
field_lsb = csr_field_detail_dict.get("lsb")
field_size = field_msb - field_lsb + 1
if field_type != "WPRI":
val_bitarray = bitarray(uint=field_val, length=field_size)
mask_bitarray = bitarray(uint=1, length=1) * field_size
start_pos = xlen - 1 - field_msb
end_pos = xlen - 1 - field_lsb
csr_read_mask.overwrite(mask_bitarray, xlen - 1 - field_msb)
csr_value.overwrite(val_bitarray, xlen - 1 - field_msb)
access = True if field_type == "R" else False
csr_write_mask.append(
[mask_bitarray, (start_pos, end_pos), access])
csrs.update({csr_name: [csr_address, csr_value, csr_write_mask,
csr_read_mask]})
return csrs
def get_rs1_val(iteration, xlen):
"""
Calculates and returns the 3 test RS1 values that will be used
to exercise the CSR.
"""
Calculates and returns the 3 test RS1 values that will be used
to exercise the CSR.
Args:
iteration: Integer between 0 and 2 inclusive, indicates which
test value to return.
xlen: The currnet RISC-V ISA bit length.
Args:
iteration: Integer between 0 and 2 inclusive, indicates which
test value to return.
xlen: The currnet RISC-V ISA bit length.
Returns:
A bitarray encoding the value that will be written to the CSR to test it.
Will be one of 3 values:
1) 0xa5a5...
2) 0x5a5a...
3) A randomly generated number
"""
if iteration == 0:
return bitarray(hex="0x{}".format('a5'*int(xlen/8)))
elif iteration == 1:
return bitarray(hex="0x{}".format('5a'*int(xlen/8)))
elif iteration == 2:
val = bitarray(uint=0, length=xlen)
# Must randomize all 32 bits, due to randomization library limitations
for i in range(32):
bit = random.randint(0, 1)
val.set(bit, i)
return val
Returns:
A bitarray encoding the value that will be written to the CSR to test it.
Will be one of 3 values:
1) 0xa5a5...
2) 0x5a5a...
3) A randomly generated number
"""
if iteration == 0:
return bitarray(hex="0x{}".format('a5' * int(xlen / 8)))
elif iteration == 1:
return bitarray(hex="0x{}".format('5a' * int(xlen / 8)))
elif iteration == 2:
val = bitarray(uint=0, length=xlen)
# Must randomize all 32 bits, due to randomization library limitations
for i in range(32):
bit = random.randint(0, 1)
val.set(bit, i)
return val
def csr_write(val, csr_val, csr_write_mask):
"""
Performs a CSR write.
"""
Performs a CSR write.
Args:
val: A bitarray containing the value to be written.
csr_val: A bitarray containing the current CSR value.
csr_write_mask: A bitarray containing the CSR's mask.
"""
for bitslice in csr_write_mask:
read_only = bitslice[2]
start_index = bitslice[1][0]
end_index = bitslice[1][1]
length = end_index - start_index + 1
mask_val = bitslice[0]
# only write if not read only
if not read_only:
val_slice = val[start_index:end_index+1]
csr_val.overwrite(mask_val & val_slice, start_index)
Args:
val: A bitarray containing the value to be written.
csr_val: A bitarray containing the current CSR value.
csr_write_mask: A bitarray containing the CSR's mask.
"""
for bitslice in csr_write_mask:
read_only = bitslice[2]
start_index = bitslice[1][0]
end_index = bitslice[1][1]
length = end_index - start_index + 1
mask_val = bitslice[0]
# only write if not read only
if not read_only:
val_slice = val[start_index:end_index + 1]
csr_val.overwrite(mask_val & val_slice, start_index)
"""
CSR Read:
Reads the given CSR, after applying the bitmask
"""
def csr_read(csr_val, csr_read_mask):
"""
Performs a CSR read.
"""
Performs a CSR read.
Args:
csr_val: A bitarray containing the current CSR value.
csr_read_mask: A bitarray containing the CSR's read mask.
Args:
csr_val: A bitarray containing the current CSR value.
csr_read_mask: A bitarray containing the CSR's read mask.
Returns:
A bitarray of the logical AND of csr_val and csr_read_mask.
"""
return csr_val & csr_read_mask
Returns:
A bitarray of the logical AND of csr_val and csr_read_mask.
"""
return csr_val & csr_read_mask
def predict_csr_val(csr_op, rs1_val, csr_val, csr_write_mask, csr_read_mask):
"""
Predicts the CSR reference value, based on the current CSR operation.
"""
Predicts the CSR reference value, based on the current CSR operation.
Args:
csr_op: A string of the CSR operation being performed.
rs1_val: A bitarray containing the value to be written to the CSR.
csr_val: A bitarray containing the current value of the CSR.
csr_write_mask: A bitarray containing the CSR's write mask.
csr_read_mask: A bitarray containing the CSR's read mask
Args:
csr_op: A string of the CSR operation being performed.
rs1_val: A bitarray containing the value to be written to the CSR.
csr_val: A bitarray containing the current value of the CSR.
csr_write_mask: A bitarray containing the CSR's write mask.
csr_read_mask: A bitarray containing the CSR's read mask
Returns:
A hexadecimal string of the predicted CSR value.
"""
prediction = None
# create a zero bitarray to zero extend immediates
zero = bitarray(uint=0, length=csr_val.len - 5)
prediction = csr_read(csr_val, csr_read_mask)
if csr_op == 'csrrw':
csr_write(rs1_val, csr_val, csr_write_mask)
elif csr_op == 'csrrs':
csr_write(rs1_val | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrc':
csr_write((~rs1_val) & prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrwi':
zero.append(rs1_val[-5:])
csr_write(zero, csr_val, csr_write_mask)
elif csr_op == 'csrrsi':
zero.append(rs1_val[-5:])
csr_write(zero | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrci':
zero.append(rs1_val[-5:])
csr_write((~zero) & prediction, csr_val, csr_write_mask)
return "0x{}".format(prediction.hex)
Returns:
A hexadecimal string of the predicted CSR value.
"""
prediction = None
# create a zero bitarray to zero extend immediates
zero = bitarray(uint=0, length=csr_val.len - 5)
prediction = csr_read(csr_val, csr_read_mask)
if csr_op == 'csrrw':
csr_write(rs1_val, csr_val, csr_write_mask)
elif csr_op == 'csrrs':
csr_write(rs1_val | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrc':
csr_write((~rs1_val) & prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrwi':
zero.append(rs1_val[-5:])
csr_write(zero, csr_val, csr_write_mask)
elif csr_op == 'csrrsi':
zero.append(rs1_val[-5:])
csr_write(zero | prediction, csr_val, csr_write_mask)
elif csr_op == 'csrrci':
zero.append(rs1_val[-5:])
csr_write((~zero) & prediction, csr_val, csr_write_mask)
return "0x{}".format(prediction.hex)
def gen_setup(test_file):
"""
Generates the setup code for the CSR test.
"""
Generates the setup code for the CSR test.
Args:
test_file: the file containing the generated assembly code.
"""
test_file.write(".macro init\n")
test_file.write(".endm\n")
test_file.write(".section .text.init\n")
test_file.write(".globl _start\n")
test_file.write(".option norvc\n")
test_file.write("_start:\n")
Args:
test_file: the file containing the generated assembly code.
"""
test_file.write(".macro init\n")
test_file.write(".endm\n")
test_file.write(".section .text.init\n")
test_file.write(".globl _start\n")
test_file.write(".option norvc\n")
test_file.write("_start:\n")
def gen_csr_test_fail(test_file, end_addr):
"""
Generates code to handle a test failure.
This code consists of writing 1 to the GP register in an infinite loop.
The testbench will poll this register at the end of the test to detect failure.
"""
Generates code to handle a test failure.
This code consists of writing 1 to the GP register in an infinite loop.
The testbench will poll this register at the end of the test to detect failure.
Args:
test_file: the file containing the generated assembly test code.
end_addr: address that should be written to at end of test
"""
test_file.write("csr_fail:\n")
test_file.write("\tli x1, {}\n".format(TEST_FAIL))
test_file.write("\tslli x1, x1, 8\n")
test_file.write("\taddi x1, x1, {}\n".format(TEST_RESULT))
test_file.write("\tli x2, 0x{}\n".format(end_addr))
test_file.write("\tsw x1, 0(x2)\n")
test_file.write("\tj csr_fail\n")
Args:
test_file: the file containing the generated assembly test code.
end_addr: address that should be written to at end of test
"""
test_file.write("csr_fail:\n")
test_file.write("\tli x1, {}\n".format(TEST_FAIL))
test_file.write("\tslli x1, x1, 8\n")
test_file.write("\taddi x1, x1, {}\n".format(TEST_RESULT))
test_file.write("\tli x2, 0x{}\n".format(end_addr))
test_file.write("\tsw x1, 0(x2)\n")
test_file.write("\tj csr_fail\n")
def gen_csr_test_pass(test_file, end_addr):
"""
Generates code to handle test success.
This code consists of writing 2 to the GP register in an infinite loop.
The testbench will poll this register at the end of the test to detect success.
"""
Generates code to handle test success.
This code consists of writing 2 to the GP register in an infinite loop.
The testbench will poll this register at the end of the test to detect success.
Args:
test_file: the file containing the generated assembly test code.
end_addr: address that should be written to at end of test
"""
test_file.write("csr_pass:\n")
test_file.write("\tli x1, {}\n".format(TEST_PASS))
test_file.write("\tslli x1, x1, 8\n")
test_file.write("\taddi x1, x1, {}\n".format(TEST_RESULT))
test_file.write("\tli x2, 0x{}\n".format(end_addr))
test_file.write("\tsw x1, 0(x2)\n")
test_file.write("\tj csr_pass\n")
Args:
test_file: the file containing the generated assembly test code.
end_addr: address that should be written to at end of test
"""
test_file.write("csr_pass:\n")
test_file.write("\tli x1, {}\n".format(TEST_PASS))
test_file.write("\tslli x1, x1, 8\n")
test_file.write("\taddi x1, x1, {}\n".format(TEST_RESULT))
test_file.write("\tli x2, 0x{}\n".format(end_addr))
test_file.write("\tsw x1, 0(x2)\n")
test_file.write("\tj csr_pass\n")
def gen_csr_instr(original_csr_map, csr_instructions, xlen,
iterations, out, end_signature_addr):
"""
Uses the information in the map produced by get_csr_map() to generate
test CSR instructions operating on the generated random values.
"""
Uses the information in the map produced by get_csr_map() to generate
test CSR instructions operating on the generated random values.
Args:
original_csr_map: The dictionary containing CSR mappings generated by get_csr_map()
csr_instructions: A list of all supported CSR instructions in string form.
xlen: The RISC-V ISA bit length.
iterations: Indicates how many randomized test files will be generated.
out: A string containing the directory path that the tests will be generated in.
end_signature_addr: The address the test should write to upon terminating
Args:
original_csr_map: The dictionary containing CSR mappings generated by get_csr_map()
csr_instructions: A list of all supported CSR instructions in string form.
xlen: The RISC-V ISA bit length.
iterations: Indicates how many randomized test files will be generated.
out: A string containing the directory path that the tests will be generated in.
end_signature_addr: The address the test should write to upon terminating
Returns:
No explicit return value, but will write the randomized assembly test code
to the specified number of files.
"""
for i in range(iterations):
# pick two GPRs at random to act as source and destination registers
# for CSR operations
csr_map = copy.deepcopy(original_csr_map)
source_reg, dest_reg = ["x{}".format(i) for i in random.sample(range(1, 16), 2)]
csr_list = list(csr_map.keys())
with open("{}/riscv_csr_test_{}.S".format(out, i), "w") as csr_test_file:
gen_setup(csr_test_file)
for csr in csr_list:
csr_address, csr_val, csr_write_mask, csr_read_mask = csr_map.get(csr)
csr_test_file.write("\t# {}\n".format(csr))
for op in csr_instructions:
for i in range(3):
# hex string
rand_rs1_val = get_rs1_val(i, xlen)
# I type CSR instruction
first_li = ""
if op[-1] == "i":
imm = rand_rs1_val[-5:]
csr_inst = "\t{} {}, {}, 0b{}\n".format(op, dest_reg, csr_address, imm.bin)
imm_val = bitarray(uint=0, length=xlen-5)
imm_val.append(imm)
predict_li = ("\tli {}, "
"{}\n".format(source_reg, predict_csr_val(op, imm_val, csr_val, csr_write_mask, csr_read_mask)))
else:
first_li = "\tli {}, 0x{}\n".format(source_reg, rand_rs1_val.hex)
csr_inst = "\t{} {}, {}, {}\n".format(op, dest_reg, csr_address, source_reg)
predict_li = ("\tli {}, "
"{}\n".format(source_reg, predict_csr_val(op, rand_rs1_val, csr_val, csr_write_mask, csr_read_mask)))
branch_check = "\tbne {}, {}, csr_fail\n".format(source_reg, dest_reg)
csr_test_file.write(first_li)
csr_test_file.write(csr_inst)
csr_test_file.write(predict_li)
csr_test_file.write(branch_check)
"""
Returns:
No explicit return value, but will write the randomized assembly test code
to the specified number of files.
"""
for i in range(iterations):
# pick two GPRs at random to act as source and destination registers
# for CSR operations
csr_map = copy.deepcopy(original_csr_map)
source_reg, dest_reg = ["x{}".format(i) for i in
random.sample(range(1, 16), 2)]
csr_list = list(csr_map.keys())
with open("{}/riscv_csr_test_{}.S".format(out, i),
"w") as csr_test_file:
gen_setup(csr_test_file)
for csr in csr_list:
csr_address, csr_val, csr_write_mask, csr_read_mask = csr_map.get(
csr)
csr_test_file.write("\t# {}\n".format(csr))
for op in csr_instructions:
for i in range(3):
# hex string
rand_rs1_val = get_rs1_val(i, xlen)
# I type CSR instruction
first_li = ""
if op[-1] == "i":
imm = rand_rs1_val[-5:]
csr_inst = "\t{} {}, {}, 0b{}\n".format(op,
dest_reg,
csr_address,
imm.bin)
imm_val = bitarray(uint=0, length=xlen - 5)
imm_val.append(imm)
predict_li = ("\tli {}, "
"{}\n".format(source_reg,
predict_csr_val(op,
imm_val,
csr_val,
csr_write_mask,
csr_read_mask)))
else:
first_li = "\tli {}, 0x{}\n".format(source_reg,
rand_rs1_val.hex)
csr_inst = "\t{} {}, {}, {}\n".format(op, dest_reg,
csr_address,
source_reg)
predict_li = ("\tli {}, "
"{}\n".format(source_reg,
predict_csr_val(op,
rand_rs1_val,
csr_val,
csr_write_mask,
csr_read_mask)))
branch_check = "\tbne {}, {}, csr_fail\n".format(
source_reg, dest_reg)
csr_test_file.write(first_li)
csr_test_file.write(csr_inst)
csr_test_file.write(predict_li)
csr_test_file.write(branch_check)
"""
We must hardcode in one final CSR check, as the value that has last
been written to the CSR has not been tested.
"""
if csr == csr_list[-1] and op == csr_instructions[-1] and i == 2:
final_csr_read = "\tcsrr {}, {}\n".format(dest_reg, csr_address)
csrrs_read_mask = bitarray(uint=0, length=xlen)
final_li = ("\tli {}, "
"{}\n".format(source_reg, predict_csr_val('csrrs', csrrs_read_mask, csr_val, csr_write_mask, csr_read_mask)))
final_branch_check = "\tbne {}, {}, csr_fail\n".format(source_reg, dest_reg)
csr_test_file.write(final_csr_read)
csr_test_file.write(final_li)
csr_test_file.write(final_branch_check)
gen_csr_test_pass(csr_test_file, end_signature_addr)
gen_csr_test_fail(csr_test_file, end_signature_addr)
if csr == csr_list[-1] and op == csr_instructions[
-1] and i == 2:
final_csr_read = "\tcsrr {}, {}\n".format(dest_reg,
csr_address)
csrrs_read_mask = bitarray(uint=0, length=xlen)
final_li = ("\tli {}, "
"{}\n".format(source_reg,
predict_csr_val('csrrs',
csrrs_read_mask,
csr_val,
csr_write_mask,
csr_read_mask)))
final_branch_check = "\tbne {}, {}, csr_fail\n".format(
source_reg, dest_reg)
csr_test_file.write(final_csr_read)
csr_test_file.write(final_li)
csr_test_file.write(final_branch_check)
gen_csr_test_pass(csr_test_file, end_signature_addr)
gen_csr_test_fail(csr_test_file, end_signature_addr)
def main():
"""Main entry point of CSR test generation script.
"""Main entry point of CSR test generation script.
Will set up a list of all supported CSR instructions,
and seed the RNG."""
# define command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--csr_file", type=str, default="yaml/csr_template.yaml",
help="The YAML file contating descriptions of all processor supported CSRs")
parser.add_argument("--xlen", type=int, default=32,
help="Specify the ISA width, e.g. 32 or 64 or 128")
parser.add_argument("--iterations", type=int, default=1,
help="Specify how many tests to be generated")
parser.add_argument("--out", type=str, default="./",
help="Specify output directory")
parser.add_argument("--end_signature_addr", type=str, default="0",
help="Address that should be written to at end of this test")
parser.add_argument("--seed", type=int, default=None,
help="""Value used to seed the random number generator. If no value is passed in,
# define command line arguments
parser = argparse.ArgumentParser()
parser.add_argument("--csr_file", type=str,
default="yaml/csr_template.yaml",
help="The YAML file contating descriptions of all processor supported CSRs")
parser.add_argument("--xlen", type=int, default=32,
help="Specify the ISA width, e.g. 32 or 64 or 128")
parser.add_argument("--iterations", type=int, default=1,
help="Specify how many tests to be generated")
parser.add_argument("--out", type=str, default="./",
help="Specify output directory")
parser.add_argument("--end_signature_addr", type=str, default="0",
help="Address that should be written to at end of this test")
parser.add_argument("--seed", type=int, default=None,
help="""Value used to seed the random number generator. If no value is passed in,
the RNG will be seeded from an internal source of randomness.""")
args = parser.parse_args()
args = parser.parse_args()
"""All supported CSR operations"""
csr_ops = ['csrrw', 'csrrs', 'csrrc', 'csrrwi', 'csrrsi', 'csrrci']
"""All supported CSR operations"""
csr_ops = ['csrrw', 'csrrs', 'csrrc', 'csrrwi', 'csrrsi', 'csrrci']
"""
Seed the RNG.
If args.seed is None, seed will be drawn from some internal random source.
If args.seed is defined, this will be used to seed the RNG for user reproducibility.
"""
random.seed(args.seed)
"""
Seed the RNG.
If args.seed is None, seed will be drawn from some internal random source.
If args.seed is defined, this will be used to seed the RNG for user reproducibility.
"""
random.seed(args.seed)
gen_csr_instr(get_csr_map(args.csr_file, args.xlen),
csr_ops, args.xlen, args.iterations, args.out,
args.end_signature_addr)
gen_csr_instr(get_csr_map(args.csr_file, args.xlen),
csr_ops, args.xlen, args.iterations, args.out,
args.end_signature_addr)
if __name__ == "__main__":
main()
main()

View file

@ -25,194 +25,201 @@ sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from riscv_trace_csv import *
def compare_trace_csv(csv1, csv2, name1, name2, log,
in_order_mode = 1,
coalescing_limit = 0,
verbose = 0,
mismatch_print_limit = 5,
compare_final_value_only = 0):
"""Compare two trace CSV file"""
matched_cnt = 0
mismatch_cnt = 0
# ensure that in order mode is disabled if necessary
if compare_final_value_only:
in_order_mode = 0
if log:
fd = open(log, 'a+')
else:
fd = sys.stdout
fd.write("%s : %s\n" % (name1, csv1))
fd.write("%s : %s\n" % (name2, csv2))
with open(csv1, "r") as fd1, open(csv2, "r") as fd2:
instr_trace_1 = []
instr_trace_2 = []
trace_csv_1 = RiscvInstructionTraceCsv(fd1)
trace_csv_2 = RiscvInstructionTraceCsv(fd2)
trace_csv_1.read_trace(instr_trace_1)
trace_csv_2.read_trace(instr_trace_2)
trace_1_index = 0
trace_2_index = 0
mismatch_cnt = 0
in_order_mode=1,
coalescing_limit=0,
verbose=0,
mismatch_print_limit=5,
compare_final_value_only=0):
"""Compare two trace CSV file"""
matched_cnt = 0
if in_order_mode:
gpr_val_1 = {}
gpr_val_2 = {}
for trace in instr_trace_1:
trace_1_index += 1
if len(trace.gpr) == 0:
continue
# Check if there's a GPR change caused by this instruction
gpr_state_change_1 = check_update_gpr(trace.gpr, gpr_val_1)
if gpr_state_change_1 == 0:
continue
# Move forward the other trace until a GPR update happens
gpr_state_change_2 = 0
while (gpr_state_change_2 == 0 and trace_2_index < len(instr_trace_2)):
gpr_state_change_2 = check_update_gpr(instr_trace_2[trace_2_index].gpr,
gpr_val_2)
trace_2_index += 1
# Check if the GPR update is the same between trace 1 and 2
if gpr_state_change_2 == 0:
mismatch_cnt += 1
fd.write("Mismatch[%d]:\n[%d] %s : %s\n" %
(mismatch_cnt, trace_1_index, name1, trace.get_trace_string()))
fd.write("%0d instructions left in trace %0s\n" %
(len(instr_trace_1) - trace_1_index + 1, name1))
elif len(trace.gpr) != len(instr_trace_2[trace_2_index-1].gpr):
mismatch_cnt += 1
# print first few mismatches
if mismatch_cnt <= mismatch_print_limit:
fd.write("Mismatch[%d]:\n%s[%d] : %s\n" %
(mismatch_cnt, name1, trace_2_index - 1,
trace.get_trace_string()))
fd.write("%s[%d] : %s\n" %
(name2, trace_2_index - 1,
instr_trace_2[trace_2_index-1].get_trace_string()))
else:
found_mismatch = 0
for i in range(len(trace.gpr)):
if trace.gpr[i] != instr_trace_2[trace_2_index-1].gpr[i]:
mismatch_cnt += 1
found_mismatch = 1
# print first few mismatches
if mismatch_cnt <= mismatch_print_limit:
fd.write("Mismatch[%d]:\n%s[%d] : %s\n" %
(mismatch_cnt, name1, trace_2_index - 1,
trace.get_trace_string()))
fd.write("%s[%d] : %s\n" %
(name2, trace_2_index - 1,
instr_trace_2[trace_2_index-1].get_trace_string()))
break
if not found_mismatch:
matched_cnt += 1
# Break the loop if it reaches the end of trace 2
if trace_2_index == len(instr_trace_2):
break
# Check if there's remaining instruction that change architectural state
if trace_2_index != len(instr_trace_2):
while (trace_2_index < len(instr_trace_2)):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].gpr,
gpr_val_2)
if gpr_state_change_2 == 1:
fd.write("%0d instructions left in trace %0s\n" %
(len(instr_trace_2) - trace_2_index, name2))
mismatch_cnt += len(instr_trace_2) - trace_2_index
break
trace_2_index += 1
else:
pass
# TODO: Enable out of order comparison
# # For processors which can commit multiple instructions in one cycle, the
# # ordering between different GPR update on that cycle could be
# # non-deterministic. If multiple instructions try to update the same GPR on
# # the same cycle, these updates could be coalesced to one update.
# gpr_trace_1 = {}
# gpr_trace_2 = {}
# parse_gpr_update_from_trace(instr_trace_1, gpr_trace_1)
# parse_gpr_update_from_trace(instr_trace_2, gpr_trace_2)
# if not compare_final_value_only:
# if len(gpr_trace_1) != len(gpr_trace_2):
# fd.write("Mismatch: affected GPR count mismtach %s:%d VS %s:%d\n" %
# (name1, len(gpr_trace_1), name2, len(gpr_trace_2)))
# mismatch_cnt += 1
# for gpr in gpr_trace_1:
# coalesced_updates = 0
# if (len(gpr_trace_1[gpr]) != len(gpr_trace_2[gpr]) and
# coalescing_limit == 0):
# fd.write("Mismatch: GPR[%s] trace count mismtach %s:%d VS %s:%d\n" %
# (gpr, name1, len(gpr_trace_1[gpr]),
# name2, len(gpr_trace_2[gpr])))
# mismatch_cnt += 1
# trace_2_index = 0
# coalesced_updates = 0
# for trace_1_index in range(0, len(gpr_trace_1[gpr])-1):
# if (trace_2_index == len(gpr_trace_2[gpr])):
# break
# if int(gpr_trace_1[gpr][trace_1_index].rd_val, 16) != \
# int(gpr_trace_2[gpr][trace_2_index].rd_val, 16):
# if coalesced_updates >= coalescing_limit:
# coalesced_updates = 0
# mismatch_cnt += 1
# if mismatch_cnt <= mismatch_print_limit:
# fd.write("Mismatch:\n")
# fd.write("%s[%d] : %s\n" % (name1, trace_1_index,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# fd.write("%s[%d] : %s\n" % (name2, trace_2_index,
# gpr_trace_2[gpr][trace_2_index].get_trace_string()))
# trace_2_index += 1
# else:
# if verbose:
# fd.write("Skipping %s[%d] : %s\n" %
# (name1, trace_1_index,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# coalesced_updates += 1
# else:
# coalesced_updates = 0
# matched_cnt += 1
# if verbose:
# fd.write("Matched [%0d]: %s : %s\n" %
# (trace_1_index, name1,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# trace_2_index += 1
# # Check the final value match between the two traces
# for gpr in gpr_trace_1:
# if not compare_final_value_only:
# if (len(gpr_trace_1[gpr]) == 0 or len(gpr_trace_2[gpr]) == 0):
# mismatch_cnt += 1
# fd.write("Zero GPR[%s] updates observed: %s:%d, %s:%d\n" % (gpr,
# name1, len(gpr_trace_1[gpr]), name2, len(gpr_trace_2[gpr])))
# else:
# if not gpr_trace_2.get(gpr):
# trace = RiscvInstructionTraceEntry()
# trace.rd_val = "0"
# trace.rd = gpr
# trace.instr_str = ""
# trace.binary = ""
# trace.addr = ""
# gpr_trace_2[gpr] = [trace]
# if int(gpr_trace_1[gpr][-1].rd_val, 16) != \
# int(gpr_trace_2[gpr][-1].rd_val, 16):
# mismatch_cnt += 1
# if mismatch_cnt <= mismatch_print_limit:
# fd.write("Mismatch final value:\n")
# fd.write("%s : %s\n" % (name1, gpr_trace_1[gpr][-1].get_trace_string()))
# fd.write("%s : %s\n" % (name2, gpr_trace_2[gpr][-1].get_trace_string()))
if mismatch_cnt == 0:
compare_result = "[PASSED]: %d matched\n" % ( matched_cnt)
else:
compare_result = "[FAILED]: %d matched, %d mismatch\n" % ( matched_cnt, mismatch_cnt)
fd.write(compare_result + "\n")
mismatch_cnt = 0
# ensure that in order mode is disabled if necessary
if compare_final_value_only:
in_order_mode = 0
if log:
fd.close()
return compare_result
fd = open(log, 'a+')
else:
fd = sys.stdout
fd.write("{} : {}\n".format(name1, csv1))
fd.write("{} : {}\n".format(name2, csv2))
with open(csv1, "r") as fd1, open(csv2, "r") as fd2:
instr_trace_1 = []
instr_trace_2 = []
trace_csv_1 = RiscvInstructionTraceCsv(fd1)
trace_csv_2 = RiscvInstructionTraceCsv(fd2)
trace_csv_1.read_trace(instr_trace_1)
trace_csv_2.read_trace(instr_trace_2)
trace_1_index = 0
trace_2_index = 0
mismatch_cnt = 0
matched_cnt = 0
if in_order_mode:
gpr_val_1 = {}
gpr_val_2 = {}
for trace in instr_trace_1:
trace_1_index += 1
if len(trace.gpr) == 0:
continue
# Check if there's a GPR change caused by this instruction
gpr_state_change_1 = check_update_gpr(trace.gpr, gpr_val_1)
if gpr_state_change_1 == 0:
continue
# Move forward the other trace until a GPR update happens
gpr_state_change_2 = 0
while (gpr_state_change_2 == 0 and trace_2_index < len(
instr_trace_2)):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].gpr,
gpr_val_2)
trace_2_index += 1
# Check if the GPR update is the same between trace 1 and 2
if gpr_state_change_2 == 0:
mismatch_cnt += 1
fd.write("Mismatch[{}]:\n[{}] {} : {}\n".format(
mismatch_cnt, trace_1_index, name1,trace.get_trace_string()))
fd.write("{} instructions left in trace {}\n".format(
len(instr_trace_1) - trace_1_index + 1, name1))
elif len(trace.gpr) != len(
instr_trace_2[trace_2_index - 1].gpr):
mismatch_cnt += 1
# print first few mismatches
if mismatch_cnt <= mismatch_print_limit:
fd.write("Mismatch[{}]:\n{}[{}] : {}\n".format(
mismatch_cnt, name1, trace_2_index - 1,
trace.get_trace_string()))
fd.write("{}[{}] : {}\n".format(
name2, trace_2_index - 1,
instr_trace_2[
trace_2_index - 1].get_trace_string()))
else:
found_mismatch = 0
for i in range(len(trace.gpr)):
if trace.gpr[i] != instr_trace_2[trace_2_index - 1].gpr[i]:
mismatch_cnt += 1
found_mismatch = 1
# print first few mismatches
if mismatch_cnt <= mismatch_print_limit:
fd.write("Mismatch[{}]:\n{}[{}] : {}\n".format(
mismatch_cnt, name1, trace_2_index - 1,
trace.get_trace_string()))
fd.write("{}[{}] : {}\n".format(
name2, trace_2_index - 1,
instr_trace_2[
trace_2_index - 1].get_trace_string()))
break
if not found_mismatch:
matched_cnt += 1
# Break the loop if it reaches the end of trace 2
if trace_2_index == len(instr_trace_2):
break
# Check if there's remaining instruction that change architectural state
if trace_2_index != len(instr_trace_2):
while trace_2_index < len(instr_trace_2):
gpr_state_change_2 = check_update_gpr(
instr_trace_2[trace_2_index].gpr,
gpr_val_2)
if gpr_state_change_2 == 1:
fd.write("{} instructions left in trace {}\n".format(
len(instr_trace_2) - trace_2_index, name2))
mismatch_cnt += len(instr_trace_2) - trace_2_index
break
trace_2_index += 1
else:
pass
# TODO: Enable out of order comparison
# # For processors which can commit multiple instructions in one cycle, the
# # ordering between different GPR update on that cycle could be
# # non-deterministic. If multiple instructions try to update the same GPR on
# # the same cycle, these updates could be coalesced to one update.
# gpr_trace_1 = {}
# gpr_trace_2 = {}
# parse_gpr_update_from_trace(instr_trace_1, gpr_trace_1)
# parse_gpr_update_from_trace(instr_trace_2, gpr_trace_2)
# if not compare_final_value_only:
# if len(gpr_trace_1) != len(gpr_trace_2):
# fd.write("Mismatch: affected GPR count mismtach %s:%d VS %s:%d\n" %
# (name1, len(gpr_trace_1), name2, len(gpr_trace_2)))
# mismatch_cnt += 1
# for gpr in gpr_trace_1:
# coalesced_updates = 0
# if (len(gpr_trace_1[gpr]) != len(gpr_trace_2[gpr]) and
# coalescing_limit == 0):
# fd.write("Mismatch: GPR[%s] trace count mismtach %s:%d VS %s:%d\n" %
# (gpr, name1, len(gpr_trace_1[gpr]),
# name2, len(gpr_trace_2[gpr])))
# mismatch_cnt += 1
# trace_2_index = 0
# coalesced_updates = 0
# for trace_1_index in range(0, len(gpr_trace_1[gpr])-1):
# if (trace_2_index == len(gpr_trace_2[gpr])):
# break
# if int(gpr_trace_1[gpr][trace_1_index].rd_val, 16) != \
# int(gpr_trace_2[gpr][trace_2_index].rd_val, 16):
# if coalesced_updates >= coalescing_limit:
# coalesced_updates = 0
# mismatch_cnt += 1
# if mismatch_cnt <= mismatch_print_limit:
# fd.write("Mismatch:\n")
# fd.write("%s[%d] : %s\n" % (name1, trace_1_index,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# fd.write("%s[%d] : %s\n" % (name2, trace_2_index,
# gpr_trace_2[gpr][trace_2_index].get_trace_string()))
# trace_2_index += 1
# else:
# if verbose:
# fd.write("Skipping %s[%d] : %s\n" %
# (name1, trace_1_index,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# coalesced_updates += 1
# else:
# coalesced_updates = 0
# matched_cnt += 1
# if verbose:
# fd.write("Matched [%0d]: %s : %s\n" %
# (trace_1_index, name1,
# gpr_trace_1[gpr][trace_1_index].get_trace_string()))
# trace_2_index += 1
# # Check the final value match between the two traces
# for gpr in gpr_trace_1:
# if not compare_final_value_only:
# if (len(gpr_trace_1[gpr]) == 0 or len(gpr_trace_2[gpr]) == 0):
# mismatch_cnt += 1
# fd.write("Zero GPR[%s] updates observed: %s:%d, %s:%d\n" % (gpr,
# name1, len(gpr_trace_1[gpr]), name2, len(gpr_trace_2[gpr])))
# else:
# if not gpr_trace_2.get(gpr):
# trace = RiscvInstructionTraceEntry()
# trace.rd_val = "0"
# trace.rd = gpr
# trace.instr_str = ""
# trace.binary = ""
# trace.addr = ""
# gpr_trace_2[gpr] = [trace]
# if int(gpr_trace_1[gpr][-1].rd_val, 16) != \
# int(gpr_trace_2[gpr][-1].rd_val, 16):
# mismatch_cnt += 1
# if mismatch_cnt <= mismatch_print_limit:
# fd.write("Mismatch final value:\n")
# fd.write("%s : %s\n" % (name1, gpr_trace_1[gpr][-1].get_trace_string()))
# fd.write("%s : %s\n" % (name2, gpr_trace_2[gpr][-1].get_trace_string()))
if mismatch_cnt == 0:
compare_result = "[PASSED]: {} matched\n".format(matched_cnt)
else:
compare_result = "[FAILED]: {} matched, {} mismatch\n".format(
matched_cnt, mismatch_cnt)
fd.write(compare_result + "\n")
if log:
fd.close()
return compare_result
#def parse_gpr_update_from_trace(trace_csv, gpr_trace):
# def parse_gpr_update_from_trace(trace_csv, gpr_trace):
# prev_val = {}
# for trace in trace_csv:
# if trace.rd != "":
@ -225,57 +232,61 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
def check_update_gpr(gpr_update, gpr):
gpr_state_change = 0
for update in gpr_update:
if update == "":
return 0
item = update.split(":")
if len(item) != 2:
sys.exit("Illegal GPR update format:" + update)
rd = item[0]
rd_val = item[1]
if rd in gpr:
if rd_val != gpr[rd]:
gpr_state_change = 1
else:
if int(rd_val, 16) != 0:
gpr_state_change = 1
gpr[rd] = rd_val
return gpr_state_change
gpr_state_change = 0
for update in gpr_update:
if update == "":
return 0
item = update.split(":")
if len(item) != 2:
sys.exit("Illegal GPR update format:" + update)
rd = item[0]
rd_val = item[1]
if rd in gpr:
if rd_val != gpr[rd]:
gpr_state_change = 1
else:
if int(rd_val, 16) != 0:
gpr_state_change = 1
gpr[rd] = rd_val
return gpr_state_change
def main():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--csv_file_1", type=str, help="Instruction trace 1 CSV")
parser.add_argument("--csv_file_2", type=str, help="Instruction trace 2 CSV")
parser.add_argument("--csv_name_1", type=str, help="Instruction trace 1 name")
parser.add_argument("--csv_name_2", type=str, help="Instruction trace 2 name")
# optional arguments
parser.add_argument("--log", type=str, default="",
help="Log file")
parser.add_argument("--in_order_mode", type=int, default=1,
help="In order comparison mode")
parser.add_argument("--gpr_update_coalescing_limit", type=int, default=1,
help="Allow the core to merge multiple updates to the \
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--csv_file_1", type=str,
help="Instruction trace 1 CSV")
parser.add_argument("--csv_file_2", type=str,
help="Instruction trace 2 CSV")
parser.add_argument("--csv_name_1", type=str,
help="Instruction trace 1 name")
parser.add_argument("--csv_name_2", type=str,
help="Instruction trace 2 name")
# optional arguments
parser.add_argument("--log", type=str, default="",
help="Log file")
parser.add_argument("--in_order_mode", type=int, default=1,
help="In order comparison mode")
parser.add_argument("--gpr_update_coalescing_limit", type=int, default=1,
help="Allow the core to merge multiple updates to the \
same GPR into one. This option only applies to \
trace 2")
parser.add_argument("--mismatch_print_limit", type=int, default=5,
help="Max number of mismatches printed")
parser.add_argument("--verbose", type=int, default=0,
help="Verbose logging")
parser.add_argument("--compare_final_value_only", type=int, default=0,
help="Only compare the final value of the GPR")
parser.add_argument("--mismatch_print_limit", type=int, default=5,
help="Max number of mismatches printed")
parser.add_argument("--verbose", type=int, default=0,
help="Verbose logging")
parser.add_argument("--compare_final_value_only", type=int, default=0,
help="Only compare the final value of the GPR")
args = parser.parse_args()
args = parser.parse_args()
# Compare trace CSV
compare_trace_csv(args.csv_file_1, args.csv_file_2,
args.csv_name_1, args.csv_name_2, args.log,
args.in_order_mode, args.gpr_update_coalescing_limit,
args.verbose, args.mismatch_print_limit,
args.compare_final_value_only)
# Compare trace CSV
compare_trace_csv(args.csv_file_1, args.csv_file_2,
args.csv_name_1, args.csv_name_2, args.log,
args.in_order_mode, args.gpr_update_coalescing_limit,
args.verbose, args.mismatch_print_limit,
args.compare_final_value_only)
if __name__ == "__main__":
main()
main()

View file

@ -31,197 +31,207 @@ RET_SUCCESS = 0
RET_FAIL = 1
RET_FATAL = -1
def setup_logging(verbose):
"""Setup the root logger.
Args:
verbose: Verbose logging
"""
if verbose:
logging.basicConfig(format="%(asctime)s %(filename)s:%(lineno)-5s %(levelname)-8s %(message)s",
datefmt='%a, %d %b %Y %H:%M:%S',
level=logging.DEBUG)
else:
logging.basicConfig(format="%(asctime)s %(levelname)-8s %(message)s",
datefmt='%a, %d %b %Y %H:%M:%S',
level=logging.INFO)
def setup_logging(verbose):
"""Setup the root logger.
Args:
verbose: Verbose logging
"""
if verbose:
logging.basicConfig(
format="%(asctime)s %(filename)s:%(lineno)-5s %(levelname)-8s %(message)s",
datefmt='%a, %d %b %Y %H:%M:%S',
level=logging.DEBUG)
else:
logging.basicConfig(format="%(asctime)s %(levelname)-8s %(message)s",
datefmt='%a, %d %b %Y %H:%M:%S',
level=logging.INFO)
def read_yaml(yaml_file):
""" Read YAML file to a dictionary
""" Read YAML file to a dictionary
Args:
yaml_file : YAML file
Args:
yaml_file : YAML file
Returns:
yaml_data : data read from YAML in dictionary format
"""
with open(yaml_file, "r") as f:
Returns:
yaml_data : data read from YAML in dictionary format
"""
with open(yaml_file, "r") as f:
try:
yaml_data = yaml.safe_load(f)
except yaml.YAMLError as exc:
logging.error(exc)
sys.exit(RET_FAIL)
return yaml_data
def get_env_var(var, debug_cmd=None):
"""Get the value of environment variable
Args:
var : Name of the environment variable
Returns:
val : Value of the environment variable
"""
try:
yaml_data = yaml.safe_load(f)
except yaml.YAMLError as exc:
logging.error(exc)
sys.exit(RET_FAIL)
return yaml_data
val = os.environ[var]
except KeyError:
if debug_cmd:
return var
else:
logging.warning("Please set the environment variable {}".format(var))
sys.exit(RET_FAIL)
return val
def get_env_var(var, debug_cmd = None):
"""Get the value of environment variable
def run_cmd(cmd, timeout_s=999, exit_on_error=1, check_return_code=True,
debug_cmd=None):
"""Run a command and return output
Args:
var : Name of the environment variable
Args:
cmd : shell command to run
Returns:
val : Value of the environment variable
"""
try:
val = os.environ[var]
except KeyError:
Returns:
command output
"""
logging.debug(cmd)
if debug_cmd:
return var
else:
logging.warning("Please set the environment variable %0s" % var)
sys.exit(RET_FAIL)
return val
def run_cmd(cmd, timeout_s = 999, exit_on_error = 1, check_return_code = True, debug_cmd = None):
"""Run a command and return output
Args:
cmd : shell command to run
Returns:
command output
"""
logging.debug(cmd)
if debug_cmd:
debug_cmd.write(cmd)
debug_cmd.write("\n\n")
return
try:
ps = subprocess.Popen("exec " + cmd,
shell=True,
executable='/bin/bash',
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
logging.error(ps.communicate()[0])
sys.exit(RET_FAIL)
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
try:
output = ps.communicate(timeout = timeout_s)[0]
except subprocess.TimeoutExpired:
logging.error("Timeout[%ds]: %s" % (timeout_s, cmd))
output = ""
ps.kill()
rc = ps.returncode
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error("ERROR return code: %d/%d, cmd:%s" % (check_return_code, rc, cmd))
if exit_on_error:
sys.exit(RET_FAIL)
logging.debug(output)
return output
def run_parallel_cmd(cmd_list, timeout_s = 999, exit_on_error = 0,
check_return_code = True, debug_cmd = None):
"""Run a list of commands in parallel
Args:
cmd_list: command list
Returns:
command output
"""
if debug_cmd:
for cmd in cmd_list:
debug_cmd.write(cmd)
debug_cmd.write("\n\n")
return
children = []
for cmd in cmd_list:
ps = subprocess.Popen("exec " + cmd,
shell=True,
executable='/bin/bash',
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
children.append(ps)
for i in range(len(children)):
logging.info("Command progress: %d/%d" % (i, len(children)))
logging.debug("Waiting for command: %s" % cmd_list[i])
debug_cmd.write(cmd)
debug_cmd.write("\n\n")
return
try:
output = children[i].communicate(timeout = timeout_s)[0]
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
except subprocess.TimeoutExpired:
logging.error("Timeout[%ds]: %s" % (timeout_s, cmd))
children[i].kill()
rc = children[i].returncode
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error("ERROR return code: %d, cmd:%s" % (rc, cmd))
if exit_on_error:
ps = subprocess.Popen("exec " + cmd,
shell=True,
executable='/bin/bash',
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
except subprocess.CalledProcessError:
logging.error(ps.communicate()[0])
sys.exit(RET_FAIL)
# Restore stty setting otherwise the terminal may go crazy
os.system("stty sane")
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
try:
output = ps.communicate(timeout=timeout_s)[0]
except subprocess.TimeoutExpired:
logging.error("Timeout[{}s]: {}".format(timeout_s, cmd))
output = ""
ps.kill()
rc = ps.returncode
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error(
"ERROR return code: {}/{}, cmd:{}".format(check_return_code, rc, cmd))
if exit_on_error:
sys.exit(RET_FAIL)
logging.debug(output)
return output
def run_cmd_output(cmd, debug_cmd = None):
"""Run a command and return output
Args:
cmd : Command line to execute
"""
logging.debug(" ".join(cmd))
if debug_cmd:
debug_cmd.write(" ".join(cmd))
debug_cmd.write("\n\n")
return
try:
output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as exc:
logging.debug(exc.output)
raise exc
sys.exit(RET_FAIL)
if output:
logging.debug(output)
def process_regression_list(testlist, test, iterations, matched_list, riscv_dv_root):
""" Get the matched tests from the regression test list
def run_parallel_cmd(cmd_list, timeout_s=999, exit_on_error=0,
check_return_code=True, debug_cmd=None):
"""Run a list of commands in parallel
Args:
testlist : Regression test list
test : Test to run, "all" means all tests in the list
iterations : Number of iterations for each test
riscv_dv_root : Root directory of RISCV-DV
Args:
cmd_list: command list
Returns:
matched_list : A list of matched tests
"""
logging.info("Processing regression test list : %s, test: %s" % (testlist, test))
yaml_data = read_yaml(testlist)
mult_test = test.split(',')
for entry in yaml_data:
if 'import' in entry:
sub_list = re.sub('<riscv_dv_root>', riscv_dv_root, entry['import'])
process_regression_list(sub_list, test, iterations, matched_list, riscv_dv_root)
else:
if (entry['test'] in mult_test) or (test == "all"):
if (iterations > 0 and entry['iterations'] > 0):
entry['iterations'] = iterations
if entry['iterations'] > 0:
logging.info("Found matched tests: %s, iterations:%0d" %
(entry['test'], entry['iterations']))
matched_list.append(entry)
Returns:
command output
"""
if debug_cmd:
for cmd in cmd_list:
debug_cmd.write(cmd)
debug_cmd.write("\n\n")
return
children = []
for cmd in cmd_list:
ps = subprocess.Popen("exec " + cmd,
shell=True,
executable='/bin/bash',
universal_newlines=True,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT)
children.append(ps)
for i in range(len(children)):
logging.info("Command progress: {}/{}".format(i, len(children)))
logging.debug("Waiting for command: {}".format(cmd_list[i]))
try:
output = children[i].communicate(timeout=timeout_s)[0]
except KeyboardInterrupt:
logging.info("\nExited Ctrl-C from user request.")
sys.exit(130)
except subprocess.TimeoutExpired:
logging.error("Timeout[{}s]: {}".format(timeout_s, cmd))
children[i].kill()
rc = children[i].returncode
if rc and check_return_code and rc > 0:
logging.info(output)
logging.error("ERROR return code: {}, cmd:{}".format(rc, cmd))
if exit_on_error:
sys.exit(RET_FAIL)
# Restore stty setting otherwise the terminal may go crazy
os.system("stty sane")
logging.debug(output)
def create_output(output, noclean, prefix = "out_"):
""" Create output directory
def run_cmd_output(cmd, debug_cmd=None):
"""Run a command and return output
Args:
cmd : Command line to execute
"""
logging.debug(" ".join(cmd))
if debug_cmd:
debug_cmd.write(" ".join(cmd))
debug_cmd.write("\n\n")
return
try:
output = subprocess.check_output(cmd)
except subprocess.CalledProcessError as exc:
logging.debug(exc.output)
raise exc
sys.exit(RET_FAIL)
if output:
logging.debug(output)
def process_regression_list(testlist, test, iterations, matched_list,
riscv_dv_root):
""" Get the matched tests from the regression test list
Args:
testlist : Regression test list
test : Test to run, "all" means all tests in the list
iterations : Number of iterations for each test
riscv_dv_root : Root directory of RISCV-DV
Returns:
matched_list : A list of matched tests
"""
logging.info(
"Processing regression test list : {}, test: {}".format(testlist, test))
yaml_data = read_yaml(testlist)
mult_test = test.split(',')
for entry in yaml_data:
if 'import' in entry:
sub_list = re.sub('<riscv_dv_root>', riscv_dv_root, entry['import'])
process_regression_list(sub_list, test, iterations, matched_list,
riscv_dv_root)
else:
if (entry['test'] in mult_test) or (test == "all"):
if iterations > 0 and entry['iterations'] > 0:
entry['iterations'] = iterations
if entry['iterations'] > 0:
logging.info("Found matched tests: {}, iterations:{}".format(
entry['test'], entry['iterations']))
matched_list.append(entry)
def create_output(output, noclean, prefix="out_"):
""" Create output directory
Args:
output : Name of specified output directory
@ -230,187 +240,356 @@ def create_output(output, noclean, prefix = "out_"):
Returns:
Output directory
"""
# Create output directory
if output is None:
output = prefix + str(date.today())
if noclean is False:
os.system("rm -rf %s" % output)
# Create output directory
if output is None:
output = prefix + str(date.today())
if noclean is False:
os.system("rm -rf {}".format(output))
logging.info("Creating output directory: {}".format(output))
subprocess.run(["mkdir", "-p", output])
return output
logging.info("Creating output directory: %s" % output)
subprocess.run(["mkdir", "-p", output])
return output
def gpr_to_abi(gpr):
"""Convert a general purpose register to its corresponding abi name"""
switcher = {
"x0" : "zero",
"x1" : "ra",
"x2" : "sp",
"x3" : "gp",
"x4" : "tp",
"x5" : "t0",
"x6" : "t1",
"x7" : "t2",
"x8" : "s0",
"x9" : "s1",
"x10" : "a0",
"x11" : "a1",
"x12" : "a2",
"x13" : "a3",
"x14" : "a4",
"x15" : "a5",
"x16" : "a6",
"x17" : "a7",
"x18" : "s2",
"x19" : "s3",
"x20" : "s4",
"x21" : "s5",
"x22" : "s6",
"x23" : "s7",
"x24" : "s8",
"x25" : "s9",
"x26" : "s10",
"x27" : "s11",
"x28" : "t3",
"x29" : "t4",
"x30" : "t5",
"x31" : "t6",
"f0" : "ft0",
"f1" : "ft1",
"f2" : "ft2",
"f3" : "ft3",
"f4" : "ft4",
"f5" : "ft5",
"f6" : "ft6",
"f7" : "ft7",
"f8" : "fs0",
"f9" : "fs1",
"f10" : "fa0",
"f11" : "fa1",
"f12" : "fa2",
"f13" : "fa3",
"f14" : "fa4",
"f15" : "fa5",
"f16" : "fa6",
"f17" : "fa7",
"f18" : "fs2",
"f19" : "fs3",
"f20" : "fs4",
"f21" : "fs5",
"f22" : "fs6",
"f23" : "fs7",
"f24" : "fs8",
"f25" : "fs9",
"f26" : "fs10",
"f27" : "fs11",
"f28" : "ft8",
"f29" : "ft9",
"f30" : "ft10",
"f31" : "ft11",
}
return switcher.get(gpr, "na")
"""Convert a general purpose register to its corresponding abi name"""
switcher = {
"x0" : "zero",
"x1" : "ra",
"x2" : "sp",
"x3" : "gp",
"x4" : "tp",
"x5" : "t0",
"x6" : "t1",
"x7" : "t2",
"x8" : "s0",
"x9" : "s1",
"x10": "a0",
"x11": "a1",
"x12": "a2",
"x13": "a3",
"x14": "a4",
"x15": "a5",
"x16": "a6",
"x17": "a7",
"x18": "s2",
"x19": "s3",
"x20": "s4",
"x21": "s5",
"x22": "s6",
"x23": "s7",
"x24": "s8",
"x25": "s9",
"x26": "s10",
"x27": "s11",
"x28": "t3",
"x29": "t4",
"x30": "t5",
"x31": "t6",
"f0" : "ft0",
"f1" : "ft1",
"f2" : "ft2",
"f3" : "ft3",
"f4" : "ft4",
"f5" : "ft5",
"f6" : "ft6",
"f7" : "ft7",
"f8" : "fs0",
"f9" : "fs1",
"f10": "fa0",
"f11": "fa1",
"f12": "fa2",
"f13": "fa3",
"f14": "fa4",
"f15": "fa5",
"f16": "fa6",
"f17": "fa7",
"f18": "fs2",
"f19": "fs3",
"f20": "fs4",
"f21": "fs5",
"f22": "fs6",
"f23": "fs7",
"f24": "fs8",
"f25": "fs9",
"f26": "fs10",
"f27": "fs11",
"f28": "ft8",
"f29": "ft9",
"f30": "ft10",
"f31": "ft11",
}
return switcher.get(gpr, "na")
def sint_to_hex(val):
"""Signed integer to hex conversion"""
return str(hex((val + (1 << 32)) % (1 << 32)))
"""Signed integer to hex conversion"""
return str(hex((val + (1 << 32)) % (1 << 32)))
BASE_RE = re.compile(
r"(?P<rd>[a-z0-9]+?),(?P<imm>[\-0-9]*?)\((?P<rs1>[a-z0-9]+?)\)")
BASE_RE = re.compile(r"(?P<rd>[a-z0-9]+?),(?P<imm>[\-0-9]*?)\((?P<rs1>[a-z0-9]+?)\)")
def convert_pseudo_instr(instr_name, operands, binary):
"""Convert pseudo instruction to regular instruction"""
if instr_name == "nop":
instr_name = "addi"
operands = "zero,zero,0"
elif instr_name == "mv":
instr_name = "addi"
operands = operands + ",0"
elif instr_name == "not":
instr_name = "xori"
operands = operands + ",-1"
elif instr_name == "neg":
instr_name = "sub"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "negw":
instr_name = "subw"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "sext.w":
instr_name = "addiw"
operands = operands + ",0"
elif instr_name == "seqz":
instr_name = "sltiu"
operands = operands + ",1"
elif instr_name == "snez":
instr_name = "sltu"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "sltz":
instr_name = "slt"
operands = operands + ",zero"
elif instr_name == "sgtz":
instr_name = "slt"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name in ["beqz", "bnez", "bgez", "bltz"]:
instr_name = instr_name[0:3]
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "blez":
instr_name = "bge";
operands = "zero," + operands
elif instr_name == "bgtz":
instr_name = "blt";
operands = "zero," + operands
elif instr_name == "bgt":
instr_name = "blt";
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "ble":
instr_name = "bge";
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "bgtu":
instr_name = "bltu";
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "bleu":
instr_name = "bgeu";
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "csrr":
instr_name = "csrrw"
operands = operands + ",zero"
elif instr_name in ["csrw", "csrs", "csrc"]:
instr_name = "csrr" + instr_name[3:]
operands = "zero," + operands
elif instr_name in ["csrwi", "csrsi", "csrci"]:
instr_name = "csrr" + instr_name[3:]
operands = "zero," + operands
elif instr_name == "jr":
instr_name = "jalr"
operands = "zero,%s,0" % operands
elif instr_name == "j":
instr_name = "jal"
operands = "zero,%s" % operands
elif instr_name == "jal":
if not ("," in operands):
operands = "ra,%s" % operands
elif instr_name == "jalr":
m = BASE_RE.search(operands)
# jalr rd, imm(rs1)
if m:
operands = "%s,%s,%s" % (m.group("rd"), m.group("rs1"), m.group("imm"))
# jalr rs1
idx = operands.rfind(",")
if idx == -1:
operands = "ra," + operands + ",0"
elif instr_name == "ret":
if binary[-1] == "2":
instr_name = "c.jr"
operands = "ra"
else:
instr_name = "jalr"
operands = "zero,ra,0"
return instr_name, operands
"""Convert pseudo instruction to regular instruction"""
if instr_name == "nop":
instr_name = "addi"
operands = "zero,zero,0"
elif instr_name == "mv":
instr_name = "addi"
operands = operands + ",0"
elif instr_name == "not":
instr_name = "xori"
operands = operands + ",-1"
elif instr_name == "neg":
instr_name = "sub"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "negw":
instr_name = "subw"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "sext.w":
instr_name = "addiw"
operands = operands + ",0"
elif instr_name == "seqz":
instr_name = "sltiu"
operands = operands + ",1"
elif instr_name == "snez":
instr_name = "sltu"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "sltz":
instr_name = "slt"
operands = operands + ",zero"
elif instr_name == "sgtz":
instr_name = "slt"
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name in ["beqz", "bnez", "bgez", "bltz"]:
instr_name = instr_name[0:3]
o = operands.split(",")
operands = o[0] + ",zero," + o[1]
elif instr_name == "blez":
instr_name = "bge"
operands = "zero," + operands
elif instr_name == "bgtz":
instr_name = "blt"
operands = "zero," + operands
elif instr_name == "bgt":
instr_name = "blt"
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "ble":
instr_name = "bge"
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "bgtu":
instr_name = "bltu"
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "bleu":
instr_name = "bgeu"
o = operands.split(",")
operands = o[1] + "," + o[0] + "," + o[2]
elif instr_name == "csrr":
instr_name = "csrrw"
operands = operands + ",zero"
elif instr_name in ["csrw", "csrs", "csrc"]:
instr_name = "csrr" + instr_name[3:]
operands = "zero," + operands
elif instr_name in ["csrwi", "csrsi", "csrci"]:
instr_name = "csrr" + instr_name[3:]
operands = "zero," + operands
elif instr_name == "jr":
instr_name = "jalr"
operands = "zero,{},0".format(operands)
elif instr_name == "j":
instr_name = "jal"
operands = "zero,{}".format(operands)
elif instr_name == "jal":
if not ("," in operands):
operands = "ra,{}".format(operands)
elif instr_name == "jalr":
m = BASE_RE.search(operands)
# jalr rd, imm(rs1)
if m:
operands = "{},{},{}".format(m.group("rd"), m.group("rs1"), m.group("imm"))
# jalr rs1
idx = operands.rfind(",")
if idx == -1:
operands = "ra," + operands + ",0"
elif instr_name == "ret":
if binary[-1] == "2":
instr_name = "c.jr"
operands = "ra"
else:
instr_name = "jalr"
operands = "zero,ra,0"
# RV32B pseudo instructions
# TODO: support "rev", "orc", and "zip/unzip" instructions for RV64
elif instr_name == "rev.p":
instr_name = "grevi"
operands += ",1"
elif instr_name == "rev2.n":
instr_name = "grevi"
operands += ",2"
elif instr_name == "rev.n":
instr_name = "grevi"
operands += ",3"
elif instr_name == "rev4.b":
instr_name = "grevi"
operands += ",4"
elif instr_name == "rev2.b":
instr_name = "grevi"
operands += ",6"
elif instr_name == "rev.b":
instr_name = "grevi"
operands += ",7"
elif instr_name == "rev8.h":
instr_name = "grevi"
operands += ",8"
elif instr_name == "rev4.h":
instr_name = "grevi"
operands += ",12"
elif instr_name == "rev2.h":
instr_name = "grevi"
operands += ",14"
elif instr_name == "rev.h":
instr_name = "grevi"
operands += ",15"
elif instr_name == "rev16":
instr_name = "grevi"
operands += ",16"
elif instr_name == "rev8":
instr_name = "grevi"
operands += ",24"
elif instr_name == "rev4":
instr_name = "grevi"
operands += ",28"
elif instr_name == "rev2":
instr_name = "grevi"
operands += ",30"
elif instr_name == "rev":
instr_name = "grevi"
operands += ",31"
elif instr_name == "orc.p":
instr_name = "gorci"
operands += ",1"
elif instr_name == "orc2.n":
instr_name = "gorci"
operands += ",2"
elif instr_name == "orc.n":
instr_name = "gorci"
operands += ",3"
elif instr_name == "orc4.b":
instr_name = "gorci"
operands += ",4"
elif instr_name == "orc2.b":
instr_name = "gorci"
operands += ",6"
elif instr_name == "orc.b":
instr_name = "gorci"
operands += ",7"
elif instr_name == "orc8.h":
instr_name = "gorci"
operands += ",8"
elif instr_name == "orc4.h":
instr_name = "gorci"
operands += ",12"
elif instr_name == "orc2.h":
instr_name = "gorci"
operands += ",14"
elif instr_name == "orc.h":
instr_name = "gorci"
operands += ",15"
elif instr_name == "orc16":
instr_name = "gorci"
operands += ",16"
elif instr_name == "orc8":
instr_name = "gorci"
operands += ",24"
elif instr_name == "orc4":
instr_name = "gorci"
operands += ",28"
elif instr_name == "orc2":
instr_name = "gorci"
operands += ",30"
elif instr_name == "orc":
instr_name = "gorci"
operands += ",31"
elif instr_name == "zext.b":
instr_name = "andi"
operands += ",255"
elif instr_name == "zext.h":
# TODO: support for RV64B
instr_name = "pack"
operands += ",zero"
elif instr_name == "zext.w":
instr_name = "pack"
operands += ",zero"
elif instr_name == "sext.w":
instr_name = "addiw"
operands += ",0"
elif instr_name == "zip.n":
instr_name = "shfli"
operands += ",1"
elif instr_name == "unzip.n":
instr_name = "unshfli"
operands += ",1"
elif instr_name == "zip2.b":
instr_name = "shfli"
operands += ",2"
elif instr_name == "unzip2.b":
instr_name = "unshfli"
operands += ",2"
elif instr_name == "zip.b":
instr_name = "shfli"
operands += ",3"
elif instr_name == "unzip.b":
instr_name = "unshfli"
operands += ",3"
elif instr_name == "zip4.h":
instr_name = "shfli"
operands += ",4"
elif instr_name == "unzip4.h":
instr_name = "unshfli"
operands += ",4"
elif instr_name == "zip2.h":
instr_name = "shfli"
operands += ",6"
elif instr_name == "unzip2.h":
instr_name = "unshfli"
operands += ",6"
elif instr_name == "zip.h":
instr_name = "shfli"
operands += ",7"
elif instr_name == "unzip.h":
instr_name = "unshfli"
operands += ",7"
elif instr_name == "zip8":
instr_name = "shfli"
operands += ",8"
elif instr_name == "unzip8":
instr_name = "unshfli"
operands += ",8"
elif instr_name == "zip4":
instr_name = "shfli"
operands += ",12"
elif instr_name == "unzip4":
instr_name = "unshfli"
operands += ",12"
elif instr_name == "zip2":
instr_name = "shfli"
operands += ",14"
elif instr_name == "unzip2":
instr_name = "unshfli"
operands += ",14"
elif instr_name == "zip":
instr_name = "shfli"
operands += ",15"
elif instr_name == "unzip":
instr_name = "unshfli"
operands += ",15"
return instr_name, operands

View file

@ -27,186 +27,200 @@ from lib import *
from riscv_trace_csv import *
INSTR_RE = re.compile(r"riscvOVPsim.*, 0x(?P<addr>.*?)(?P<section>\(.*\): ?)" \
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr_str>.*?)$")
"(?P<mode>[A-Za-z]*?)\s+(?P<bin>[a-f0-9]*?)\s+(?P<instr_str>.*?)$")
RD_RE = re.compile(r" (?P<r>[a-z]*[0-9]{0,2}?) (?P<pre>[a-f0-9]+?)" \
" -> (?P<val>[a-f0-9]+?)$")
BASE_RE = re.compile(r"(?P<rd>[a-z0-9]+?),(?P<imm>[\-0-9]*?)\((?P<rs1>[a-z0-9]+?)\)")
" -> (?P<val>[a-f0-9]+?)$")
BASE_RE = re.compile(
r"(?P<rd>[a-z0-9]+?),(?P<imm>[\-0-9]*?)\((?P<rs1>[a-z0-9]+?)\)")
def convert_mode(pri, line, stop_on_first_error=False):
""" 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(RET_FATAL)
""" 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 [{}]: {}".format(pri, line))
if stop_on_first_error:
sys.exit(RET_FATAL)
def is_csr(r):
""" see if r is a csr """
if len(r) > 4:
return True
elif r[0] in ["m", "u", "d"]:
return True
elif r in ["frm", "fcsr", "vl", "satp"]:
return True
else:
return False
""" see if r is a csr """
if len(r) > 4:
return True
elif r[0] in ["m", "u", "d"]:
return True
elif r in ["frm", "fcsr", "vl", "satp"]:
return True
else:
return False
def process_ovpsim_sim_log(ovpsim_log, csv,
stop_on_first_error = 0,
dont_truncate_after_first_ecall = 0,
full_trace = True):
"""Process OVPsim simulation log.
stop_on_first_error=0,
dont_truncate_after_first_ecall=0,
full_trace=True):
"""Process OVPsim simulation log.
Extract instruction and affected register information from ovpsim simulation
log and save to a list.
"""
logging.info("Processing ovpsim log : %s" % ovpsim_log)
Extract instruction and affected register information from ovpsim simulation
log and save to a list.
"""
logging.info("Processing ovpsim log : {}".format(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 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)
# Remove the header part of ovpsim log
cmd = ("sed -i '/Info 1:/,$!d' {}".format(ovpsim_log))
os.system(cmd)
# Remove all instructions after end of trace data (end of program excecution)
if dont_truncate_after_first_ecall:
cmd = ("sed -i '/^Info --/q' {}".format(ovpsim_log))
logging.info("Dont truncate logfile after first ecall: {}".format(ovpsim_log))
else:
cmd = ("sed -i '/ecall/q' {}".format(ovpsim_log))
os.system(cmd)
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
for line in f:
# Extract instruction infromation
m = INSTR_RE.search(line)
if m:
if prev_trace: # write out the previous one when find next one
trace_csv.write_trace_entry(prev_trace)
instr_cnt += 1
prev_trace = 0
prev_trace = RiscvInstructionTraceEntry()
prev_trace.instr_str = m.group("instr_str")
prev_trace.pc = m.group("addr")
prev_trace.mode = convert_mode(m.group("mode"), line)
prev_trace.binary = m.group("bin")
if full_trace:
prev_trace.instr = prev_trace.instr_str.split(" ")[0]
prev_trace.operand = prev_trace.instr_str[len(prev_trace.instr):]
prev_trace.operand = prev_trace.operand.replace(" ", "")
process_trace(prev_trace)
continue
# Extract register change value information
c = RD_RE.search(line)
if c:
if is_csr(c.group("r")):
prev_trace.csr.append(c.group("r") + ":" + c.group("val"))
else:
prev_trace.gpr.append(c.group("r") + ":" + c.group("val"))
logging.info("Processed instruction count : %d " % instr_cnt)
if instr_cnt == 0:
logging.error ("No Instructions in logfile: %s" % ovpsim_log)
sys.exit(RET_FATAL)
logging.info("CSV saved to : %s" % csv)
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
for line in f:
# Extract instruction infromation
m = INSTR_RE.search(line)
if m:
if prev_trace: # write out the previous one when find next one
trace_csv.write_trace_entry(prev_trace)
instr_cnt += 1
prev_trace = 0
prev_trace = RiscvInstructionTraceEntry()
prev_trace.instr_str = m.group("instr_str")
prev_trace.pc = m.group("addr")
prev_trace.mode = convert_mode(m.group("mode"), line)
prev_trace.binary = m.group("bin")
if full_trace:
prev_trace.instr = prev_trace.instr_str.split(" ")[0]
prev_trace.operand = prev_trace.instr_str[
len(prev_trace.instr):]
prev_trace.operand = prev_trace.operand.replace(" ", "")
process_trace(prev_trace)
continue
# Extract register change value information
c = RD_RE.search(line)
if c:
if is_csr(c.group("r")):
prev_trace.csr.append(c.group("r") + ":" + c.group("val"))
else:
prev_trace.gpr.append(c.group("r") + ":" + c.group("val"))
logging.info("Processed instruction count : {} ".format(instr_cnt))
if instr_cnt == 0:
logging.error("No Instructions in logfile: {}".format(ovpsim_log))
sys.exit(RET_FATAL)
logging.info("CSV saved to : {}".format(csv))
def process_trace(trace):
""" Process instruction operands """
process_compressed_instr(trace)
process_imm(trace)
if trace.instr == "jalr":
process_jalr(trace)
trace.instr, trace.operand = convert_pseudo_instr(
trace.instr, trace.operand, trace.binary)
trace.operand = trace.operand.replace(")", "")
trace.operand = trace.operand.replace("(", ",")
""" Process instruction operands """
process_compressed_instr(trace)
process_imm(trace)
if trace.instr == "jalr":
process_jalr(trace)
trace.instr, trace.operand = convert_pseudo_instr(
trace.instr, trace.operand, trace.binary)
# process any instruction of the form:
# <instr> <reg> <imm>(<reg>)
m = BASE_RE.search(trace.operand)
if m:
trace.operand = "{},{},{}".format(
m.group("rd"), m.group("rs1"), m.group("imm"))
def process_imm(trace):
""" Process imm to follow RISC-V standard convention """
if trace.instr in ['beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu', 'c.beqz',
'c.bnez', 'beqz', 'bnez', 'bgez', 'bltz', 'blez', 'bgtz'
'c.j', "j", "c.jal", "jal"]:
# convert from ovpsim logs branch/jump offsets as absolute to relative
idx = trace.operand.rfind(",")
if idx == -1:
imm = trace.operand
imm = str(sint_to_hex(int(imm, 16) - int(trace.pc, 16)))
trace.operand = imm
else:
imm = trace.operand[idx+1:]
imm = str(sint_to_hex(int(imm, 16) - int(trace.pc, 16)))
trace.operand = trace.operand[0:idx+1] + imm
""" Process imm to follow RISC-V standard convention """
if trace.instr in ['beq', 'bne', 'blt', 'bge', 'bltu', 'bgeu', 'c.beqz',
'c.bnez', 'beqz', 'bnez', 'bgez', 'bltz', 'blez', 'bgtz',
'c.j','j', 'c.jal', 'jal']:
# convert from ovpsim logs branch/jump offsets as absolute to relative
idx = trace.operand.rfind(",")
if idx == -1:
imm = trace.operand
imm = str(sint_to_hex(int(imm, 16) - int(trace.pc, 16)))
trace.operand = imm
else:
imm = trace.operand[idx + 1:]
imm = str(sint_to_hex(int(imm, 16) - int(trace.pc, 16)))
trace.operand = trace.operand[0:idx + 1] + imm
def process_jalr(trace):
""" process jalr """
## jalr x3
## jalr 9(x3)
## jalr x2,x3
## jalr x2,4(x3)
idx = trace.operand.rfind(",")
if idx == -1:
# Add default destination register : ra
trace.operand = "ra," + trace.operand
m = BASE_RE.search(trace.operand)
if m:
# Convert pseudo JALR format to normal format
trace.operand = "%s,%s,%s" % (m.group("rd"), m.group("rs1"), m.group("imm"))
else:
# Add default imm 0
trace.operand = trace.operand + ",0"
""" process jalr """
## jalr x3
## jalr 9(x3)
## jalr x2,x3
## jalr x2,4(x3)
idx = trace.operand.rfind(",")
if idx == -1:
# Add default destination register : ra
trace.operand = "ra," + trace.operand
m = BASE_RE.search(trace.operand)
if m:
# Convert pseudo JALR format to normal format
trace.operand = "{},{},{}".format(
m.group("rd"), m.group("rs1"), m.group("imm"))
else:
# Add default imm 0
trace.operand = trace.operand + ",0"
def process_compressed_instr(trace):
""" convert naming for compressed instructions """
trace_binary = int(trace.binary, 16)
o = trace.operand.split(",")
if len(trace.binary) == 4: # compressed are always 4 hex digits
trace.instr = "c." + trace.instr
if ("sp,sp," in trace.operand) and (trace.instr == "c.addi"):
trace.instr = "c.addi16sp"
idx = trace.operand.rfind(",")
trace.operand = "sp," + trace.operand[idx+1:]
elif (",sp," in trace.operand) and (trace.instr == "c.addi"):
trace.instr = "c.addi4spn"
elif ("(sp)" in trace.operand) and (trace_binary % 4 != 0):
trace.instr = trace.instr + "sp"
if not ("(" in trace.operand):
# OVPSIM use non-compressed instruction format in the trace,
# need to remove duplicated rs1/rd
if len(o) > 2:
trace.operand = ",".join(o[1:])
if trace.instr == "c.jal":
trace.operand = o[1]
""" convert naming for compressed instructions """
trace_binary = int(trace.binary, 16)
o = trace.operand.split(",")
if len(trace.binary) == 4: # compressed are always 4 hex digits
trace.instr = "c." + trace.instr
if ("sp,sp," in trace.operand) and (trace.instr == "c.addi"):
trace.instr = "c.addi16sp"
idx = trace.operand.rfind(",")
trace.operand = "sp," + trace.operand[idx + 1:]
elif (",sp," in trace.operand) and (trace.instr == "c.addi"):
trace.instr = "c.addi4spn"
elif ("(sp)" in trace.operand) and (trace_binary % 4 != 0):
trace.instr = trace.instr + "sp"
if not ("(" in trace.operand):
# OVPSIM use non-compressed instruction format in the trace,
# need to remove duplicated rs1/rd
if len(o) > 2:
trace.operand = ",".join(o[1:])
if trace.instr == "c.jal":
trace.operand = o[1]
def main():
""" if used standalone set up for testing """
# 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("--verbose", dest="verbose", action="store_true",
help="Verbose logging")
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(verbose=False)
parser.set_defaults(stop_on_first_error=False)
parser.set_defaults(dont_truncate_after_first_ecall=False)
args = parser.parse_args()
# Process ovpsim log
process_ovpsim_sim_log(args.log,
args.csv,
args.stop_on_first_error,
args.dont_truncate_after_first_ecall)
""" if used standalone set up for testing """
# 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("--verbose", dest="verbose", action="store_true",
help="Verbose logging")
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(verbose=False)
parser.set_defaults(stop_on_first_error=False)
parser.set_defaults(dont_truncate_after_first_ecall=False)
args = parser.parse_args()
# Process ovpsim log
process_ovpsim_sim_log(args.log,
args.csv,
args.stop_on_first_error,
args.dont_truncate_after_first_ecall)
if __name__ == "__main__":
main()
main()

View file

@ -22,77 +22,80 @@ import logging
import sys
from lib import *
class RiscvInstructionTraceEntry(object):
"""RISC-V instruction trace entry"""
def __init__(self):
self.gpr = []
self.csr = []
self.instr = ""
self.operand = ""
self.pc = ""
self.binary = ""
self.instr_str = ""
self.mode = ""
def get_trace_string(self):
"""Return a short string of the trace entry"""
return ("pc[%s] %s: %s %s" %
(self.pc, self.instr_str, " ".join(self.gpr), " ".join(self.csr)))
class RiscvInstructionTraceEntry(object):
"""RISC-V instruction trace entry"""
def __init__(self):
self.gpr = []
self.csr = []
self.instr = ""
self.operand = ""
self.pc = ""
self.binary = ""
self.instr_str = ""
self.mode = ""
def get_trace_string(self):
"""Return a short string of the trace entry"""
return ("pc[{}] {}: {} {}".format(
self.pc, self.instr_str, " ".join(self.gpr), " ".join(self.csr)))
class RiscvInstructionTraceCsv(object):
"""RISC-V instruction trace CSV class
"""RISC-V instruction trace CSV class
This class provides functions to read/write trace CSV
"""
def __init__(self, csv_fd):
self.csv_fd = csv_fd
This class provides functions to read/write trace CSV
"""
def __init__(self, csv_fd):
self.csv_fd = csv_fd
def start_new_trace(self):
"""Create a CSV file handle for a new trace"""
fields = [
"pc", "instr", "gpr", "csr", "binary", "mode", "instr_str", "operand", "pad"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
def start_new_trace(self):
"""Create a CSV file handle for a new trace"""
fields = ["pc", "instr", "gpr", "csr", "binary", "mode", "instr_str",
"operand", "pad"]
self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
self.csv_writer.writeheader()
def read_trace(self, trace):
"""Read instruction trace from CSV file"""
csv_reader = csv.DictReader(self.csv_fd)
for row in csv_reader:
new_trace = RiscvInstructionTraceEntry()
new_trace.gpr = row['gpr'].split(';')
new_trace.csr = row['csr'].split(';')
new_trace.pc = row['pc']
new_trace.operand = row['operand']
new_trace.binary = row['binary']
new_trace.instr_str = row['instr_str']
new_trace.instr = row['instr']
new_trace.mode = row['mode']
trace.append(new_trace)
def read_trace(self, trace):
"""Read instruction trace from CSV file"""
csv_reader = csv.DictReader(self.csv_fd)
for row in csv_reader:
new_trace = RiscvInstructionTraceEntry()
new_trace.gpr = row['gpr'].split(';')
new_trace.csr = row['csr'].split(';')
new_trace.pc = row['pc']
new_trace.operand = row['operand']
new_trace.binary = row['binary']
new_trace.instr_str = row['instr_str']
new_trace.instr = row['instr']
new_trace.mode = row['mode']
trace.append(new_trace)
# TODO: Convert pseudo instruction to regular instruction
# TODO: Convert pseudo instruction to regular instruction
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.csv_writer.writerow({'instr_str': entry.instr_str,
'gpr' : ";".join(entry.gpr),
'csr' : ";".join(entry.csr),
'operand' : entry.operand,
'pc' : entry.pc,
'binary' : entry.binary,
'instr' : entry.instr,
'mode' : entry.mode})
def write_trace_entry(self, entry):
"""Write a new trace entry to CSV"""
self.csv_writer.writerow({'instr_str' : entry.instr_str,
'gpr' : ";".join(entry.gpr),
'csr' : ";".join(entry.csr),
'operand' : entry.operand,
'pc' : entry.pc,
'binary' : entry.binary,
'instr' : entry.instr,
'mode' : entry.mode})
def get_imm_hex_val(imm):
"""Get the hex representation of the imm value"""
if imm[0] == '-':
is_negative = 1
imm = imm[1:]
else:
is_negative = 0
imm_val = int(imm, 0)
if is_negative:
imm_val = -imm_val
hexstr = sint_to_hex(imm_val)
return hexstr[2:]
"""Get the hex representation of the imm value"""
if imm[0] == '-':
is_negative = 1
imm = imm[1:]
else:
is_negative = 0
imm_val = int(imm, 0)
if is_negative:
imm_val = -imm_val
hexstr = sint_to_hex(imm_val)
return hexstr[2:]

View file

@ -27,69 +27,72 @@ sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from riscv_trace_csv import *
START_RE = re.compile(r"\[4\] \[M\]: 0x.*00001010")
END_RE = re.compile(r"ecall")
END_RE = re.compile(r"ecall")
INSTR_RE = re.compile(r"\[[0-9].*\] \[(?P<pri>.)\]: 0x(?P<addr>[A-F0-9]+?)"
" \(0x(?P<bin>[A-F0-9]+?)\) (?P<instr>.+?$)")
RD_RE = re.compile(r"x(?P<reg>[0-9]+?) <- 0x(?P<val>[A-F0-9]*)")
" \(0x(?P<bin>[A-F0-9]+?)\) (?P<instr>.+?$)")
RD_RE = re.compile(r"x(?P<reg>[0-9]+?) <- 0x(?P<val>[A-F0-9]*)")
def process_sail_sim_log(sail_log, csv):
"""Process SAIL RISCV simulation log.
"""Process SAIL RISCV simulation log.
Extract instruction and affected register information from sail simulation
log and save to a list.
"""
logging.info("Processing sail log : %s" % sail_log)
instr_cnt = 0
Extract instruction and affected register information from sail simulation
log and save to a list.
"""
logging.info("Processing sail log : {}".format(sail_log))
instr_cnt = 0
with open(sail_log, "r") as f, open(csv, "w") as csv_fd:
search_start = 0
instr_start = 0
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
instr = None
for line in f:
# Extract instruction infromation
m = START_RE.search(line)
if m:
search_start = 1
continue
m = END_RE.search(line)
if m:
break
if search_start:
instr = INSTR_RE.search(line)
if instr:
instr_start = 1
pri = instr.group("pri")
addr = instr.group("addr").lower()
binary = instr.group("bin").lower()
instr_str = instr.group("instr")
continue
if instr_start:
m = RD_RE.search(line)
if m:
# Write the extracted instruction to a csvcol buffer file
instr_cnt += 1
rv_instr_trace = RiscvInstructionTraceEntry()
rv_instr_trace.gpr.append(
gpr_to_abi("x%0s" % m.group("reg")) + ":" + m.group("val").lower())
rv_instr_trace.mode = pri
rv_instr_trace.pc = addr
rv_instr_trace.binary = binary
rv_instr_trace.instr_str = instr_str
trace_csv.write_trace_entry(rv_instr_trace)
instr_start = 0
logging.info("Processed instruction count : %d" % instr_cnt)
with open(sail_log, "r") as f, open(csv, "w") as csv_fd:
search_start = 0
instr_start = 0
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
instr = None
for line in f:
# Extract instruction infromation
m = START_RE.search(line)
if m:
search_start = 1
continue
m = END_RE.search(line)
if m:
break
if search_start:
instr = INSTR_RE.search(line)
if instr:
instr_start = 1
pri = instr.group("pri")
addr = instr.group("addr").lower()
binary = instr.group("bin").lower()
instr_str = instr.group("instr")
continue
if instr_start:
m = RD_RE.search(line)
if m:
# Write the extracted instruction to a csvcol buffer file
instr_cnt += 1
rv_instr_trace = RiscvInstructionTraceEntry()
rv_instr_trace.gpr.append(
gpr_to_abi("x{}".format(m.group("reg"))) + ":" + m.group(
"val").lower())
rv_instr_trace.mode = pri
rv_instr_trace.pc = addr
rv_instr_trace.binary = binary
rv_instr_trace.instr_str = instr_str
trace_csv.write_trace_entry(rv_instr_trace)
instr_start = 0
logging.info("Processed instruction count : {}".format(instr_cnt))
def main():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input sail simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process sail log
process_sail_sim_log(args.log, args.csv)
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input sail simulation log")
parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
args = parser.parse_args()
# Process sail log
process_sail_sim_log(args.log, args.csv)
if __name__ == "__main__":
main()
main()

View file

@ -27,209 +27,220 @@ sys.path.insert(0, os.path.dirname(os.path.realpath(__file__)))
from riscv_trace_csv import *
from lib import *
RD_RE = re.compile(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) (?P<reg>[xf]\s*\d*?) 0x(?P<val>[a-f0-9]+)")
CORE_RE = re.compile(r"core.*0x(?P<addr>[a-f0-9]+?) \(0x(?P<bin>.*?)\) (?P<instr>.*?)$")
ILLE_RE = re.compile(r"trap_illegal_instruction")
RD_RE = re.compile(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) (?P<reg>[xf]\s*\d*?) 0x(?P<val>[a-f0-9]+)")
CORE_RE = re.compile(
r"core.*0x(?P<addr>[a-f0-9]+?) \(0x(?P<bin>.*?)\) (?P<instr>.*?)$")
ADDR_RE = re.compile(
r"(?P<rd>[a-z0-9]+?),(?P<imm>[\-0-9]+?)\((?P<rs1>[a-z0-9]+)\)")
ILLE_RE = re.compile(r"trap_illegal_instruction")
LOGGER = logging.getLogger()
def process_instr(trace):
if trace.instr == "jal":
# Spike jal format jal rd, -0xf -> jal rd, -15
idx = trace.operand.rfind(",")
imm = trace.operand[idx+1:]
if imm[0] == "-":
imm = "-" + str(int(imm[1:], 16))
else:
imm = str(int(imm, 16))
trace.operand = trace.operand[0:idx+1] + imm
trace.operand = trace.operand.replace("(", ",")
trace.operand = trace.operand.replace(")", "")
if trace.instr == "jal":
# Spike jal format jal rd, -0xf -> jal rd, -15
idx = trace.operand.rfind(",")
imm = trace.operand[idx + 1:]
if imm[0] == "-":
imm = "-" + str(int(imm[1:], 16))
else:
imm = str(int(imm, 16))
trace.operand = trace.operand[0:idx + 1] + imm
# Properly format operands of all instructions of the form:
# <instr> <reg1> <imm>(<reg2>)
# The operands should be converted into CSV as:
# "<reg1>,<reg2>,<imm>"
m = ADDR_RE.search(trace.operand)
if m:
trace.operand = "{},{},{}".format(
m.group("rd"), m.group("rs1"), m.group("imm"))
def read_spike_instr(match, full_trace):
'''Unpack a regex match for CORE_RE to a RiscvInstructionTraceEntry
"""Unpack a regex match for CORE_RE to a RiscvInstructionTraceEntry
If full_trace is true, extract operand data from the disassembled
instruction.
If full_trace is true, extract operand data from the disassembled
instruction.
'''
"""
# Extract the disassembled instruction.
disasm = match.group('instr')
# Extract the disassembled instruction.
disasm = match.group('instr')
# Spike's disassembler shows a relative jump as something like "j pc +
# 0x123" or "j pc - 0x123". We just want the relative offset.
disasm = disasm.replace('pc + ', '').replace('pc - ', '-')
# Spike's disassembler shows a relative jump as something like "j pc +
# 0x123" or "j pc - 0x123". We just want the relative offset.
disasm = disasm.replace('pc + ', '').replace('pc - ', '-')
instr = RiscvInstructionTraceEntry()
instr.pc = match.group('addr')
instr.instr_str = disasm
instr.binary = match.group('bin')
instr = RiscvInstructionTraceEntry()
instr.pc = match.group('addr')
instr.instr_str = disasm
instr.binary = match.group('bin')
if full_trace:
opcode = disasm.split(' ')[0]
operand = disasm[len(opcode):].replace(' ', '')
instr.instr, instr.operand = \
convert_pseudo_instr(opcode, operand, instr.binary)
if full_trace:
opcode = disasm.split(' ')[0]
operand = disasm[len(opcode):].replace(' ', '')
instr.instr, instr.operand = \
convert_pseudo_instr(opcode, operand, instr.binary)
process_instr(instr)
process_instr(instr)
return instr
return instr
def read_spike_trace(path, full_trace):
'''Read a Spike simulation log at <path>, yielding executed instructions.
"""Read a Spike simulation log at <path>, yielding executed instructions.
This assumes that the log was generated with the -l and --log-commits options
to Spike.
This assumes that the log was generated with the -l and --log-commits options
to Spike.
If full_trace is true, extract operands from the disassembled instructions.
If full_trace is true, extract operands from the disassembled instructions.
Since Spike has a strange trampoline that always runs at the start, we skip
instructions up to and including the one at PC 0x1010 (the end of the
trampoline). At the end of a DV program, there's an ECALL instruction, which
we take as a signal to stop checking, so we ditch everything that follows
that instruction.
Since Spike has a strange trampoline that always runs at the start, we skip
instructions up to and including the one at PC 0x1010 (the end of the
trampoline). At the end of a DV program, there's an ECALL instruction, which
we take as a signal to stop checking, so we ditch everything that follows
that instruction.
This function yields instructions as it parses them as tuples of the form
(entry, illegal). entry is a RiscvInstructionTraceEntry. illegal is a
boolean, which is true if the instruction caused an illegal instruction trap.
This function yields instructions as it parses them as tuples of the form
(entry, illegal). entry is a RiscvInstructionTraceEntry. illegal is a
boolean, which is true if the instruction caused an illegal instruction trap.
'''
"""
# This loop is a simple FSM with states TRAMPOLINE, INSTR, EFFECT. The idea
# is that we're in state TRAMPOLINE until we get to the end of Spike's
# trampoline, then we switch between INSTR (where we expect to read an
# instruction) and EFFECT (where we expect to read commit information).
#
# We yield a RiscvInstructionTraceEntry object each time we leave EFFECT
# (going back to INSTR), we loop back from INSTR to itself, or we get to the
# end of the file and have an instruction in hand.
#
# On entry to the loop body, we are in state TRAMPOLINE if in_trampoline is
# true. Otherwise, we are in state EFFECT if instr is not None, otherwise we
# are in state INSTR.
# This loop is a simple FSM with states TRAMPOLINE, INSTR, EFFECT. The idea
# is that we're in state TRAMPOLINE until we get to the end of Spike's
# trampoline, then we switch between INSTR (where we expect to read an
# instruction) and EFFECT (where we expect to read commit information).
#
# We yield a RiscvInstructionTraceEntry object each time we leave EFFECT
# (going back to INSTR), we loop back from INSTR to itself, or we get to the
# end of the file and have an instruction in hand.
#
# On entry to the loop body, we are in state TRAMPOLINE if in_trampoline is
# true. Otherwise, we are in state EFFECT if instr is not None, otherwise we
# are in state INSTR.
end_trampoline_re = re.compile(r'core.*: 0x0*1010 ')
end_trampoline_re = re.compile(r'core.*: 0x0*1010 ')
in_trampoline = True
instr = None
in_trampoline = True
instr = None
with open(path, 'r') as handle:
for line in handle:
if in_trampoline:
# The TRAMPOLINE state
if end_trampoline_re.match(line):
in_trampoline = False
continue
with open(path, 'r') as handle:
for line in handle:
if in_trampoline:
# The TRAMPOLINE state
if end_trampoline_re.match(line):
in_trampoline = False
continue
if instr is None:
# The INSTR state. We expect to see a line matching CORE_RE. We'll
# discard any other lines.
instr_match = CORE_RE.match(line)
if not instr_match:
continue
if instr is None:
# The INSTR state. We expect to see a line matching CORE_RE.
# We'll discard any other lines.
instr_match = CORE_RE.match(line)
if not instr_match:
continue
instr = read_spike_instr(instr_match, full_trace)
instr = read_spike_instr(instr_match, full_trace)
# If instr.instr_str is 'ecall', we should stop.
if instr.instr_str == 'ecall':
break
# If instr.instr_str is 'ecall', we should stop.
if instr.instr_str == 'ecall':
break
continue
continue
# The EFFECT state. If the line matches CORE_RE, we should have been in
# state INSTR, so we yield the instruction we had, read the new
# instruction and continue. As above, if the new instruction is 'ecall',
# we need to stop immediately.
instr_match = CORE_RE.match(line)
if instr_match:
yield (instr, False)
instr = read_spike_instr(instr_match, full_trace)
if instr.instr_str == 'ecall':
break
continue
# The EFFECT state. If the line matches CORE_RE, we should have been in
# state INSTR, so we yield the instruction we had, read the new
# instruction and continue. As above, if the new instruction is 'ecall',
# we need to stop immediately.
instr_match = CORE_RE.match(line)
if instr_match:
yield instr, False
instr = read_spike_instr(instr_match, full_trace)
if instr.instr_str == 'ecall':
break
continue
# The line doesn't match CORE_RE, so we are definitely on a follow-on
# line in the log. First, check for illegal instructions
if 'trap_illegal_instruction' in line:
yield (instr, True)
instr = None
continue
# The line doesn't match CORE_RE, so we are definitely on a follow-on
# line in the log. First, check for illegal instructions
if 'trap_illegal_instruction' in line:
yield (instr, True)
instr = None
continue
# The instruction seems to have been fine. Do we have commit data (from
# the --log-commits Spike option)?
commit_match = RD_RE.match(line)
if commit_match:
instr.gpr.append(gpr_to_abi(commit_match.group('reg')
.replace(' ', '')) +
':' + commit_match.group('val'))
instr.mode = commit_match.group('pri')
# The instruction seems to have been fine. Do we have commit data (from
# the --log-commits Spike option)?
commit_match = RD_RE.match(line)
if commit_match:
instr.gpr.append(gpr_to_abi(commit_match.group('reg')
.replace(' ', '')) +
':' + commit_match.group('val'))
instr.mode = commit_match.group('pri')
# At EOF, we might have an instruction in hand. Yield it if so.
if instr is not None:
yield (instr, False)
# At EOF, we might have an instruction in hand. Yield it if so.
if instr is not None:
yield (instr, False)
def process_spike_sim_log(spike_log, csv, full_trace = 0):
"""Process SPIKE simulation log.
def process_spike_sim_log(spike_log, csv, full_trace=0):
"""Process SPIKE simulation log.
Extract instruction and affected register information from spike simulation
log and write the results to a CSV file at csv. Returns the number of
instructions written.
Extract instruction and affected register information from spike simulation
log and write the results to a CSV file at csv. Returns the number of
instructions written.
"""
logging.info("Processing spike log : %s" % spike_log)
instrs_in = 0
instrs_out = 0
"""
logging.info("Processing spike log : {}".format(spike_log))
instrs_in = 0
instrs_out = 0
with open(csv, "w") as csv_fd:
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
with open(csv, "w") as csv_fd:
trace_csv = RiscvInstructionTraceCsv(csv_fd)
trace_csv.start_new_trace()
for (entry, illegal) in read_spike_trace(spike_log, full_trace):
instrs_in += 1
for (entry, illegal) in read_spike_trace(spike_log, full_trace):
instrs_in += 1
if illegal and full_trace:
logging.debug("Illegal instruction: {}, opcode:{}"
.format(entry.instr_str, entry.binary))
if illegal and full_trace:
logging.debug("Illegal instruction: {}, opcode:{}"
.format(entry.instr_str, entry.binary))
# Instructions that cause no architectural update (which includes illegal
# instructions) are ignored if full_trace is false.
#
# We say that an instruction caused an architectural update if either we
# saw a commit line (in which case, entry.gpr will contain a single
# entry) or the instruction was 'wfi' or 'ecall'.
if not (full_trace or entry.gpr or entry.instr_str in ['wfi', 'ecall']):
continue
# Instructions that cause no architectural update (which includes illegal
# instructions) are ignored if full_trace is false.
#
# We say that an instruction caused an architectural update if either we
# saw a commit line (in which case, entry.gpr will contain a single
# entry) or the instruction was 'wfi' or 'ecall'.
if not (full_trace or entry.gpr or entry.instr_str in ['wfi',
'ecall']):
continue
trace_csv.write_trace_entry(entry)
instrs_out += 1
trace_csv.write_trace_entry(entry)
instrs_out += 1
logging.info("Processed instruction count : %d" % instrs_in)
logging.info("CSV saved to : %s" % csv)
return instrs_out
logging.info("Processed instruction count : {}".format(instrs_in))
logging.info("CSV saved to : {}".format(csv))
return instrs_out
def main():
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input spike 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 spike log
process_spike_sim_log(args.log, args.csv, args.full_trace)
# Parse input arguments
parser = argparse.ArgumentParser()
parser.add_argument("--log", type=str, help="Input spike 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 spike log
process_spike_sim_log(args.log, args.csv, args.full_trace)
if __name__ == "__main__":
main()
main()

View file

@ -27,63 +27,67 @@ 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>.*?)$")
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 = ""
def process_whisper_sim_log(whisper_log, csv, full_trace=0):
"""Process SPIKE simulation log.
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.gpr.append(gpr_to_abi(reg) + ":" + 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)
Extract instruction and affected register information from whisper simulation
log and save to a list.
"""
logging.info("Processing whisper log : {}".format(whisper_log))
instr_cnt = 0
whisper_instr = ""
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: {}, pc:{}, bin:{}, instr:{}".format(
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.gpr.append(
gpr_to_abi(reg) + ":" + m.group("val"))
trace_csv.write_trace_entry(rv_instr_trace)
instr_cnt += 1
logging.info("Processed instruction count : {}".format(instr_cnt))
logging.info("CSV saved to : {}".format(csv))
def main():
# 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)
# 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()
main()

View file

@ -268,10 +268,10 @@
I_FORMAT: begin
`DV_CHECK_FATAL(operands.size() == 3, instr_name)
if(category == LOAD) begin
// load rd, imm(rs1)
rs1 = get_gpr(operands[2]);
rs1_value = get_gpr_state(operands[2]);
get_val(operands[1], imm);
// load rd, imm(rs1) -> rd,rs1,imm
rs1 = get_gpr(operands[1]);
rs1_value = get_gpr_state(operands[1]);
get_val(operands[2], imm);
end else if(category == CSR) begin
// csrrwi rd, csr, imm
get_val(operands[2], imm);
@ -290,11 +290,12 @@
S_FORMAT, B_FORMAT: begin
`DV_CHECK_FATAL(operands.size() == 3)
if(category == STORE) begin
rs2 = get_gpr(operands[0]);
rs2_value = get_gpr_state(operands[0]);
rs1 = get_gpr(operands[2]);
rs1_value = get_gpr_state(operands[2]);
get_val(operands[1], imm);
// store rs2, imm(rs1) -> rs1,rs2,imm
rs2 = get_gpr(operands[1]);
rs2_value = get_gpr_state(operands[1]);
rs1 = get_gpr(operands[0]);
rs1_value = get_gpr_state(operands[0]);
get_val(operands[2], imm);
end else begin
// bne rs1, rs2, imm
rs1 = get_gpr(operands[0]);
@ -358,18 +359,18 @@
end
end
CL_FORMAT: begin
// c.lw rd, imm(rs1)
get_val(operands[1], imm);
rs1 = get_gpr(operands[2]);
rs1_value = get_gpr_state(operands[2]);
// c.lw rd, imm(rs1) -> rd,rs1,imm
get_val(operands[2], imm);
rs1 = get_gpr(operands[1]);
rs1_value = get_gpr_state(operands[1]);
end
CS_FORMAT: begin
// c.sw rs2,imm(rs1)
rs2 = get_gpr(operands[0]);
rs2_value = get_gpr_state(operands[0]);
rs1 = get_gpr(operands[2]);
rs1_value = get_gpr_state(operands[2]);
get_val(operands[1], imm);
// c.sw rs2,imm(rs1) -> rs1,rs2,imm
rs2 = get_gpr(operands[1]);
rs2_value = get_gpr_state(operands[1]);
rs1 = get_gpr(operands[0]);
rs1_value = get_gpr_state(operands[0]);
get_val(operands[2], imm);
end
CA_FORMAT: begin
// c.and rd, rs2 (rs1 == rd)

View file

@ -26,6 +26,7 @@ class riscv_vector_instr extends riscv_floating_point_instr;
rand va_variant_t va_variant;
rand bit vm;
rand bit wd;
rand bit [10:0] eew;
bit has_vd = 1'b1;
bit has_vs1 = 1'b1;
bit has_vs2 = 1'b1;
@ -39,6 +40,7 @@ class riscv_vector_instr extends riscv_floating_point_instr;
va_variant_t allowed_va_variants[$];
string sub_extension;
rand bit [2:0] nfields; // Used by segmented load/store
rand bit [3:0] emul;
constraint avoid_reserved_vregs_c {
if (m_cfg.vector_cfg.reserved_vregs.size() > 0) {
@ -56,6 +58,7 @@ class riscv_vector_instr extends riscv_floating_point_instr;
// Instructions specifying a vector operand with an odd-numbered vector register will raisean
// illegal instruction exception.
// TODO: Exclude the instruction that ignore VLMUL
// TODO: Update this constraint for fractional LMUL
constraint operand_group_c {
if (m_cfg.vector_cfg.vtype.vlmul > 0) {
vd % m_cfg.vector_cfg.vtype.vlmul == 0;
@ -268,6 +271,38 @@ class riscv_vector_instr extends riscv_floating_point_instr;
}
}
// load/store EEW/EMUL and corresponding register grouping constraints
constraint load_store_solve_order_c {
solve eew before emul;
solve emul before vd;
solve emul before vs1;
solve emul before vs2;
solve emul before vs3;
}
constraint load_store_eew_emul_c {
if (category inside {LOAD, STORE, AMO}) {
eew inside {m_cfg.vector_cfg.legal_eew};
if (eew > m_cfg.vector_cfg.vtype.vsew) {
emul == eew / m_cfg.vector_cfg.vtype.vsew;
} else {
emul == 1;
}
if (emul > 1) {
vd % emul == 0;
vs1 % emul == 0;
vs2 % emul == 0;
vs3 % emul == 0;
}
}
}
// Some temporarily constraint to avoid illegal instruction
// TODO: Review these constraints
constraint temp_c {
(vm == 0) -> (vd != 0);
}
`uvm_object_utils(riscv_vector_instr)
`uvm_object_new
@ -303,6 +338,22 @@ class riscv_vector_instr extends riscv_floating_point_instr;
return 1'b1;
endfunction
virtual function string get_instr_name();
string name = super.get_instr_name();
if (category inside {LOAD, STORE}) begin
// Add eew before ".v" or "ff.v" suffix
if (instr_name inside {VLEFF_V, VLSEGEFF_V}) begin
name = name.substr(0, name.len() - 5);
name = $sformatf("%0s%0dFF.V", name, eew);
end else begin
name = name.substr(0, name.len() - 3);
name = $sformatf("%0s%0d.V", name, eew);
end
`uvm_info(`gfn, $sformatf("%0s -> %0s", super.get_instr_name(), name), UVM_LOW)
end
return name;
endfunction
// Convert the instruction to assembly code
virtual function string convert2asm(string prefix = "");
string asm_str;
@ -448,6 +499,9 @@ class riscv_vector_instr extends riscv_floating_point_instr;
vs2.rand_mode(has_vs2);
vs3.rand_mode(has_vs3);
vd.rand_mode(has_vd);
if (!(category inside {LOAD, STORE, AMO})) begin
load_store_solve_order_c.constraint_mode(0);
end
endfunction : pre_randomize
virtual function void set_rand_mode();
@ -504,9 +558,14 @@ class riscv_vector_instr extends riscv_floating_point_instr;
string suffix = instr_name.substr(prefix.len(), instr_name.len() - 1);
return $sformatf("%0s%0d%0s", prefix, nfields + 1, suffix);
endfunction
function string add_eew(string instr_name, string prefix);
string suffix = instr_name.substr(prefix.len(), instr_name.len() - 1);
return $sformatf("%0s%0d%0s", prefix, eew, suffix);
endfunction
function bit check_sub_extension(string s, string literal);
return s == literal;
endfunction
endclass : riscv_vector_instr

View file

@ -205,114 +205,32 @@
// Section 7.4 - Vector Unit-Stride Instructions
`DEFINE_VA_INSTR(VLE_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSE_V, VS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VLB_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSB_V, VS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VLH_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSH_V, VS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VLW_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSW_V, VS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VLBU_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLHU_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLWU_V, VL_FORMAT, LOAD, RVV)
// Section 7.5 - Vector Strided Instructions
`DEFINE_VA_INSTR(VLSB_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSH_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSW_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSBU_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSHU_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSWU_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLSE_V, VLS_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSSB_V, VSS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSSH_V, VSS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSSW_V, VSS_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSSE_V, VSS_FORMAT, STORE, RVV)
// Section 7.6 - Vector Indexed Instructions
`DEFINE_VA_INSTR(VLXB_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXH_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXW_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXBU_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXHU_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXWU_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLXE_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSXB_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSXH_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSXW_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSXE_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSUXB_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSUXH_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSUXW_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSUXE_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VLXEI_V, VLX_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VSXEI_V, VSX_FORMAT, STORE, RVV)
`DEFINE_VA_INSTR(VSUXEI_V, VSX_FORMAT, STORE, RVV)
// Section 7.7 - Vector Unit-Stride Fault-Only-First Loads
`DEFINE_VA_INSTR(VLBFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLHFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLWFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLBUFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLHUFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLWUFF_V, VL_FORMAT, LOAD, RVV)
`DEFINE_VA_INSTR(VLEFF_V, VL_FORMAT, LOAD, RVV)
// Section 7.8 - Vector Load/Store Segment Instructions (Zvlsseg)
// 7.8.1. Vector Unit Strided Segment Loads and Stores
`DEFINE_VA_INSTR(VLSEGE_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSEGE_V, VS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGB_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSEGB_V, VS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGH_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSEGH_V, VS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGW_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSEGW_V, VS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGBU_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGHU_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGWU_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGBFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGHFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGWFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGBUFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGHUFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGWUFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSEGEFF_V, VL_FORMAT, LOAD, RVV, {}, "zvlsseg")
// 7.8.2. Vector Strided Segment Loads and Stores
`DEFINE_VA_INSTR(VLSSEGB_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGH_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGW_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGBU_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGHU_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGWU_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLSSEGE_V, VLS_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSSEGB_V, VSS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSSEGH_V, VSS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSSEGW_V, VSS_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSSSEGE_V, VSS_FORMAT, STORE, RVV, {}, "zvlsseg")
// 7.8.3. Vector Indexed Segment Loads and Stores
`DEFINE_VA_INSTR(VLXSEGB_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGH_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGW_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGBU_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGHU_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGWU_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGE_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSXSEGB_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSXSEGH_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSXSEGW_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSXSEGE_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSUXSEGB_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSUXSEGH_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSUXSEGW_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSUXSEGE_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VLXSEGEI_V, VLX_FORMAT, LOAD, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSXSEGEI_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
`DEFINE_VA_INSTR(VSUXSEGEI_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg")
// -------------------------------------------------------------------------
// Section 8. Vector AMO Operations (Zvamo)
// -------------------------------------------------------------------------
// 32-bit vector AMOs
`DEFINE_VA_INSTR(VAMOSWAPW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOADDW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOXORW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOANDW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOORW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOMINW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOMAXW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOMINUW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOMAXUW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
// SEW-bit vector AMOs
// EEW vector AMOs
`DEFINE_VA_INSTR(VAMOSWAPE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOADDE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")
`DEFINE_VA_INSTR(VAMOXORE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo")

View file

@ -148,6 +148,27 @@ class riscv_lr_sc_instr_stream extends riscv_amo_base_instr_stream;
instr_list.push_back(sc_instr);
endfunction
// section 8.3 Eventual Success of Store-Conditional Instructions
// An LR/SC sequence begins with an LR instruction and ends with an SC instruction.
// The dynamic code executed between the LR and SC instructions can only contain
// instructions from the base “I” instruction set, excluding loads, stores, backward
// jumps, taken backward branches, JALR, FENCE, and SYSTEM instructions. If the “C”
// extension is supported, then compressed forms of the aforementioned “I” instructions
// are also permitted.
virtual function void add_mixed_instr(int instr_cnt);
riscv_instr instr;
int i;
setup_allowed_instr(.no_branch(1), .no_load_store(1));
while (i < instr_cnt) begin
instr = riscv_instr::type_id::create("instr");
randomize_instr(instr, .include_group({RV32I, RV32C}));
if (!(instr.category inside {SYNCH, SYSTEM})) begin
insert_instr(instr);
i++;
end
end
endfunction
endclass : riscv_lr_sc_instr_stream
class riscv_amo_instr_stream extends riscv_amo_base_instr_stream;
@ -193,12 +214,8 @@ endclass : riscv_amo_instr_stream
class riscv_vector_amo_instr_stream extends riscv_vector_load_store_instr_stream;
constraint amo_address_mode_c {
// AMO operation only supports word alignment or element alignemt
alignment inside {W_ALIGNMENT, E_ALIGNMENT};
// AMO operation uses indexed address mode
address_mode == INDEXED;
// For the 32-bit vector AMO operations, SEW must be at least 32 bit
(cfg.vector_cfg.vtype.vsew < 32) -> (alignment != W_ALIGNMENT);
}
`uvm_object_utils(riscv_vector_amo_instr_stream)
@ -210,10 +227,4 @@ class riscv_vector_amo_instr_stream extends riscv_vector_load_store_instr_stream
VAMOMAXE_V, VAMOMINUE_V, VAMOMAXUE_V, allowed_instr};
endfunction
virtual function void add_w_vec_load_stores();
allowed_instr = {VAMOSWAPW_V, VAMOADDW_V, VAMOXORW_V,
VAMOANDW_V, VAMOORW_V, VAMOMINW_V,
VAMOMAXW_V, VAMOMINUW_V, VAMOMAXUW_V, allowed_instr};
endfunction
endclass : riscv_vector_amo_instr_stream

View file

@ -89,7 +89,9 @@ class riscv_asm_program_gen extends uvm_object;
gen_load_fault_handler(hart);
// Store fault handler
gen_store_fault_handler(hart);
gen_test_done();
if (hart == 0) begin
gen_test_done();
end
end
// Generate sub program
gen_sub_program(hart, sub_program[hart], sub_program_name, cfg.num_of_sub_program);
@ -117,7 +119,10 @@ class riscv_asm_program_gen extends uvm_object;
instr_stream = {instr_stream, main_program[hart].instr_string_list};
// If PMP is supported, need to jump from end of main program to test_done section at the end
// of main_program, as the test_done will have moved to the beginning of the program
instr_stream = {instr_stream, $sformatf("%sj test_done", indent)};
instr_stream = {instr_stream,
$sformatf("%sla x%0d, test_done", indent, cfg.scratch_reg),
$sformatf("%sjalr x0, x%0d, 0", indent, cfg.scratch_reg)
};
// Test done section
// If PMP isn't supported, generate this in the normal location
if (hart == 0 & !riscv_instr_pkg::support_pmp) begin
@ -130,10 +135,6 @@ class riscv_asm_program_gen extends uvm_object;
// Program end
gen_program_end(hart);
if (!cfg.bare_program_mode) begin
if (!riscv_instr_pkg::support_pmp) begin
// Privileged mode switch routine
gen_privileged_mode_switch_routine(hart);
end
// Generate debug rom section
if (riscv_instr_pkg::support_debug_mode) begin
gen_debug_rom(hart);
@ -329,7 +330,8 @@ class riscv_asm_program_gen extends uvm_object;
end
gen_section("_start", str);
for (int hart = 0; hart < cfg.num_of_harts; hart++) begin
instr_stream.push_back($sformatf("%0d: j h%0d_start", hart, hart));
instr_stream.push_back($sformatf("%0d: la x%0d, h%0d_start", hart, cfg.scratch_reg, hart));
instr_stream.push_back($sformatf("jalr x0, x%0d, 0", cfg.scratch_reg));
end
endfunction
@ -706,19 +708,19 @@ class riscv_asm_program_gen extends uvm_object;
// TB can check the GPR value for this memory location to compare with expected value generated
// by the ISA simulator. If the processor doesn't have a good tracer unit, it might not be
// possible to compare the GPR value after each instruction execution.
virtual function void gen_register_dump();
virtual function void gen_register_dump(ref string instr[$]);
string str;
// Load base address
str = {indent, $sformatf("la x%0d, _start", cfg.gpr[0])};
instr_stream.push_back(str);
str = $sformatf("la x%0d, _start", cfg.gpr[0]);
instr.push_back(str);
// Generate sw/sd instructions
for(int i = 0; i < 32; i++) begin
if(XLEN == 64) begin
str = {indent, $sformatf("sd x%0d, %0d(x%0d)", i, i*(XLEN/8), cfg.gpr[0])};
str = $sformatf("sd x%0d, %0d(x%0d)", i, i*(XLEN/8), cfg.gpr[0]);
end else begin
str = {indent, $sformatf("sw x%0d, %0d(x%0d)", i, i*(XLEN/8), cfg.gpr[0])};
str = $sformatf("sw x%0d, %0d(x%0d)", i, i*(XLEN/8), cfg.gpr[0]);
end
instr_stream.push_back(str);
instr.push_back(str);
end
endfunction
@ -749,14 +751,10 @@ class riscv_asm_program_gen extends uvm_object;
end
// Setup mepc register, jump to init entry
setup_epc(hart);
// Setup initial privilege mode
gen_privileged_mode_switch_routine(hart);
// Initialization of any implementation-specific custom CSRs
setup_custom_csrs(hart);
// Move privileged mode support to the "safe" section of the program
// if PMP is supported
if (riscv_instr_pkg::support_pmp) begin
// Privileged mode switch routine
gen_privileged_mode_switch_routine(hart);
end
endfunction
virtual function void gen_privileged_mode_switch_routine(int hart);
@ -817,9 +815,6 @@ class riscv_asm_program_gen extends uvm_object;
end
mode_name = cfg.init_privileged_mode.name();
instr.push_back($sformatf("csrw 0x%0x, x%0d", MEPC, cfg.gpr[0]));
if (!riscv_instr_pkg::support_pmp) begin
instr.push_back($sformatf("j %0sinit_%0s", hart_prefix(hart), mode_name.tolower()));
end
gen_section(get_label("mepc_setup", hart), instr);
endfunction
@ -1092,7 +1087,9 @@ class riscv_asm_program_gen extends uvm_object;
cfg.gpr[0], cfg.gpr[1], hart_prefix(hart)),
// Skip checking tval for illegal instruction as it's implementation specific
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[1], tval, tval.name()),
"1: jal x1, test_done "
// use JALR to jump to test_done.
$sformatf("1: la x%0d, test_done", cfg.scratch_reg),
$sformatf("jalr x1, x%0d, 0", cfg.scratch_reg)
};
gen_section(get_label($sformatf("%0smode_exception_handler", mode), hart), instr);
endfunction
@ -1138,7 +1135,8 @@ class riscv_asm_program_gen extends uvm_object;
// Jump to commmon interrupt handling routine
intr_handler = {intr_handler,
$sformatf("j %0s%0smode_intr_handler", hart_prefix(hart), mode),
"1: j test_done"};
$sformatf("1: la x%0d, test_done", cfg.scratch_reg),
$sformatf("jalr x0, x%0d, 0", cfg.scratch_reg)};
gen_section(get_label($sformatf("%0smode_intr_vector_%0d", mode, i), hart), intr_handler);
end
endfunction
@ -1147,14 +1145,12 @@ class riscv_asm_program_gen extends uvm_object;
// It does some clean up like dump GPRs before communicating with host to terminate the test.
// User can extend this function if some custom clean up routine is needed.
virtual function void gen_ecall_handler(int hart);
string str;
str = format_string(get_label("ecall_handler:", hart), LABEL_STR_LEN);
instr_stream.push_back(str);
dump_perf_stats();
gen_register_dump();
str = format_string(" ", LABEL_STR_LEN);
str = {str, "j write_tohost"};
instr_stream.push_back(str);
string instr[$];
dump_perf_stats(instr);
gen_register_dump(instr);
instr.push_back($sformatf("la x%0d, write_tohost", cfg.scratch_reg));
instr.push_back($sformatf("jalr x0, x%0d, 0", cfg.scratch_reg));
gen_section(get_label("ecall_handler", hart), instr);
endfunction
// Ebreak trap handler
@ -1410,17 +1406,14 @@ class riscv_asm_program_gen extends uvm_object;
endfunction
// Dump performance CSRs if applicable
virtual function void dump_perf_stats();
string perf_stats[$];
virtual function void dump_perf_stats(ref string instr[$]);
foreach(implemented_csr[i]) begin
if (implemented_csr[i] inside {[MCYCLE:MHPMCOUNTER31H]}) begin
gen_signature_handshake(.instr(perf_stats),
gen_signature_handshake(.instr(instr),
.signature_type(WRITE_CSR),
.csr(implemented_csr[i]));
end
end
format_section(perf_stats);
instr_stream = {instr_stream, perf_stats};
endfunction
// Write the generated program to a file
@ -1612,19 +1605,23 @@ class riscv_asm_program_gen extends uvm_object;
//---------------------------------------------------------------------------------------
virtual function void randomize_vec_gpr_and_csr();
string lmul;
if (!(RVV inside {supported_isa})) return;
instr_stream.push_back({indent, $sformatf("li x%0d, %0d", cfg.gpr[0], cfg.vector_cfg.vxsat)});
instr_stream.push_back({indent, $sformatf("csrw vxsat, x%0d", cfg.gpr[0])});
instr_stream.push_back({indent, $sformatf("li x%0d, %0d", cfg.gpr[0], cfg.vector_cfg.vxrm)});
instr_stream.push_back({indent, $sformatf("csrw vxrm, x%0d", cfg.gpr[0])});
instr_stream.push_back({indent, $sformatf("csrwi vxsat, %0d", cfg.vector_cfg.vxsat)});
instr_stream.push_back({indent, $sformatf("csrwi vxrm, %0d", cfg.vector_cfg.vxrm)});
init_vec_gpr(); // GPR init uses a temporary SEW/LMUL setting before the final value set below.
instr_stream.push_back($sformatf("li x%0d, %0d", cfg.gpr[1], cfg.vector_cfg.vl));
instr_stream.push_back($sformatf("%svsetvli x%0d, x%0d, e%0d, m%0d, d%0d",
if ((cfg.vector_cfg.vtype.vlmul > 1) && (cfg.vector_cfg.vtype.fractional_lmul)) begin
lmul = $sformatf("mf%0d", cfg.vector_cfg.vtype.vlmul);
end else begin
lmul = $sformatf("m%0d", cfg.vector_cfg.vtype.vlmul);
end
instr_stream.push_back($sformatf("%svsetvli x%0d, x%0d, e%0d, %0s, d%0d",
indent,
cfg.gpr[0],
cfg.gpr[1],
cfg.vector_cfg.vtype.vsew,
cfg.vector_cfg.vtype.vlmul,
lmul,
cfg.vector_cfg.vtype.vediv));
endfunction

View file

@ -93,8 +93,12 @@ class riscv_debug_rom_gen extends riscv_asm_program_gen;
cfg.num_debug_sub_program);
main_program[hart].post_process_instr();
main_program[hart].generate_instr_stream(.no_label(1'b1));
debug_main = {debug_main,
main_program[hart].instr_string_list,
$sformatf("%sla x%0d, debug_end", indent, cfg.scratch_reg),
$sformatf("%sjalr x0, x%0d, 0", indent, cfg.scratch_reg)
};
insert_sub_program(sub_program[hart], debug_main);
debug_main = {debug_main, main_program[hart].instr_string_list};
gen_section($sformatf("%0sdebug_rom", hart_prefix(hart)), debug_main);
if (cfg.enable_ebreak_in_debug_rom) begin
gen_ebreak_footer();

View file

@ -43,7 +43,7 @@ class riscv_instr_gen_config extends uvm_object;
// SAME_VALUES_ALL_ELEMS - Using vmv.v.x to fill all the elements of the vreg with the same value as the one in the GPR selected
// RANDOM_VALUES_VMV - Using vmv.v.x + vslide1up.vx to randomize the contents of each vector element
// RANDOM_VALUES_LOAD - Using vle.v, same approach as RANDOM_VALUES_VMV but more efficient for big VLEN
vreg_init_method_t vreg_init_method = RANDOM_VALUES_VMV;
vreg_init_method_t vreg_init_method = RANDOM_VALUES_VMV;
// Associate array for delegation configuration for each exception and interrupt
// When the bit is 1, the corresponding delegation is enabled.
@ -411,16 +411,16 @@ class riscv_instr_gen_config extends uvm_object;
!(tp inside {GP, RA, ZERO});
}
// This reg is used in various places throughout the generator,
// so need more conservative constraints on it.
constraint reserve_scratch_reg_c {
scratch_reg != ZERO;
scratch_reg != sp;
scratch_reg != tp;
!(scratch_reg inside {ZERO, sp, tp, ra, GP});
}
// This reg is only used inside PMP exception routine,
// so we can be a bit looser with constraints.
constraint reserve_pmp_reg_c {
pmp_reg != ZERO;
pmp_reg != sp;
pmp_reg != tp;
!(pmp_reg inside {ZERO, sp, tp});
}
constraint gpr_c {

View file

@ -599,105 +599,23 @@ package riscv_instr_pkg;
// Vector load/store instruction
VLE_V,
VSE_V,
VLB_V,
VSB_V,
VLH_V,
VSH_V,
VLW_V,
VSW_V,
VLBU_V,
VLHU_V,
VLWU_V,
VLSB_V,
VLSH_V,
VLSW_V,
VLSBU_V,
VLSHU_V,
VLSWU_V,
VLSE_V,
VSSB_V,
VSSH_V,
VSSW_V,
VSSE_V,
VLXB_V,
VLXH_V,
VLXW_V,
VLXBU_V,
VLXHU_V,
VLXWU_V,
VLXE_V,
VSXB_V,
VSXH_V,
VSXW_V,
VSXE_V,
VSUXB_V,
VSUXH_V,
VSUXW_V,
VSUXE_V,
VLBFF_V,
VLHFF_V,
VLWFF_V,
VLBUFF_V,
VLHUFF_V,
VLWUFF_V,
VLXEI_V,
VSXEI_V,
VSUXEI_V,
VLEFF_V,
// Segmented load/store instruction
VLSEGE_V,
VSSEGE_V,
VLSEGB_V,
VSSEGB_V,
VLSEGH_V,
VSSEGH_V,
VLSEGW_V,
VSSEGW_V,
VLSEGBFF_V,
VLSEGHFF_V,
VLSEGWFF_V,
VLSEGBUFF_V,
VLSEGHUFF_V,
VLSEGWUFF_V,
VLSEGEFF_V,
VLSEGBU_V,
VLSEGHU_V,
VLSEGWU_V,
VLSSEGB_V,
VLSSEGH_V,
VLSSEGW_V,
VLSSEGBU_V,
VLSSEGHU_V,
VLSSEGWU_V,
VLSSEGE_V,
VSSSEGB_V,
VSSSEGH_V,
VSSSEGW_V,
VSSSEGE_V,
VLXSEGB_V,
VLXSEGH_V,
VLXSEGW_V,
VLXSEGBU_V,
VLXSEGHU_V,
VLXSEGWU_V,
VLXSEGE_V,
VSXSEGB_V,
VSXSEGH_V,
VSXSEGW_V,
VSXSEGE_V,
VSUXSEGB_V,
VSUXSEGH_V,
VSUXSEGW_V,
VSUXSEGE_V,
VLXSEGEI_V,
VSXSEGEI_V,
VSUXSEGEI_V,
// Vector AMO instruction
// 32-bit vector AMOs
VAMOSWAPW_V,
VAMOADDW_V,
VAMOXORW_V,
VAMOANDW_V,
VAMOORW_V,
VAMOMINW_V,
VAMOMAXW_V,
VAMOMINUW_V,
VAMOMAXUW_V,
// SEW-bit vector AMOs
// EEW vector AMOs
VAMOSWAPE_V,
VAMOADDE_V,
VAMOXORE_V,
@ -1218,6 +1136,7 @@ package riscv_instr_pkg;
typedef struct packed {
bit ill;
bit fractional_lmul;
bit [XLEN-2:7] reserved;
int vediv;
int vsew;

View file

@ -52,7 +52,9 @@ class riscv_instr_stream extends uvm_object;
// When index is -1, the instruction is injected at a random location
function void insert_instr(riscv_instr instr, int idx = -1);
int current_instr_cnt = instr_list.size();
if(idx == -1) begin
if (current_instr_cnt == 0) begin
idx = 0;
end else if (idx == -1) begin
idx = $urandom_range(0, current_instr_cnt-1);
while(instr_list[idx].atomic) begin
idx += 1;
@ -228,17 +230,25 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
function void randomize_instr(output riscv_instr instr,
input bit is_in_debug = 1'b0,
input bit disable_dist = 1'b0);
input bit disable_dist = 1'b0,
input riscv_instr_group_t include_group[$] = {});
riscv_instr_name_t exclude_instr[];
if ((SP inside {reserved_rd, cfg.reserved_regs}) ||
((avail_regs.size() > 0) && !(SP inside {avail_regs}))) begin
exclude_instr = {C_ADDI4SPN, C_ADDI16SP, C_LWSP, C_LDSP};
end
if (is_in_debug && !cfg.enable_ebreak_in_debug_rom) begin
exclude_instr = {exclude_instr, EBREAK, C_EBREAK};
// Post-process the allowed_instr and exclude_instr lists to handle
// adding ebreak instructions to the debug rom.
if (is_in_debug) begin
if (cfg.no_ebreak && cfg.enable_ebreak_in_debug_rom) begin
allowed_instr = {allowed_instr, EBREAK, C_EBREAK};
end else if (!cfg.no_ebreak && !cfg.enable_ebreak_in_debug_rom) begin
exclude_instr = {exclude_instr, EBREAK, C_EBREAK};
end
end
instr = riscv_instr::get_rand_instr(.include_instr(allowed_instr),
.exclude_instr(exclude_instr));
.exclude_instr(exclude_instr),
.include_group(include_group));
instr.m_cfg = cfg;
randomize_gpr(instr);
endfunction

View file

@ -521,10 +521,9 @@ endclass
class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
typedef enum {B_ALIGNMENT, H_ALIGNMENT, W_ALIGNMENT, E_ALIGNMENT} alignment_e;
typedef enum {UNIT_STRIDED, STRIDED, INDEXED} address_mode_e;
rand alignment_e alignment;
rand bit [10:0] eew;
rand int unsigned data_page_id;
rand int unsigned num_mixed_instr;
rand int unsigned stride_byte_offset;
@ -538,20 +537,22 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
num_mixed_instr inside {[0:10]};
}
constraint eew_c {
eew inside {cfg.vector_cfg.legal_eew};
}
constraint stride_byte_offset_c {
solve eew before stride_byte_offset;
// Keep a reasonable byte offset range to avoid vector memory address overflow
stride_byte_offset inside {[1 : 32]};
(alignment == H_ALIGNMENT) -> (stride_byte_offset % 2 == 0);
(alignment == W_ALIGNMENT) -> (stride_byte_offset % 4 == 0);
(alignment == E_ALIGNMENT) -> (stride_byte_offset % cfg.vector_cfg.vtype.vsew == 0);
stride_byte_offset inside {[1 : 128]};
stride_byte_offset % (eew / 8) == 1;
}
constraint index_addr_c {
solve eew before index_addr;
// Keep a reasonable index address range to avoid vector memory address overflow
index_addr inside {[1 : 128]};
(alignment == H_ALIGNMENT) -> (index_addr % 2 == 0);
(alignment == W_ALIGNMENT) -> (index_addr % 4 == 0);
(alignment == E_ALIGNMENT) -> (index_addr % cfg.vector_cfg.vtype.vsew == 0);
index_addr inside {[0 : 128]};
index_addr % (eew / 8) == 1;
}
constraint vec_rs_c {
@ -564,11 +565,6 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
data_page_id < max_data_page_id;
}
constraint alignment_sew_c {
if (cfg.vector_cfg.vtype.vsew <= 16) {alignment != W_ALIGNMENT;}
if (cfg.vector_cfg.vtype.vsew <= 8) {alignment != H_ALIGNMENT;}
}
int base;
int max_load_store_addr;
riscv_vector_instr load_store_instr;
@ -576,10 +572,6 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
`uvm_object_utils(riscv_vector_load_store_instr_stream)
`uvm_object_new
virtual function int get_addr_alignment_mask(int alignment_bytes);
return alignment_bytes - 1;
endfunction
function void post_randomize();
reserved_rd = {reserved_rd, rs1_reg, rs2_reg};
randomize_avail_regs();
@ -613,17 +605,13 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
`uvm_fatal(`gfn, $sformatf({"Expected positive value for max_load_store_addr, got %0d.",
" Perhaps more memory needs to be allocated in the data pages for vector loads and stores.",
"\ndata_page_id:%0d\ndata_page[data_page_id].size_in_bytes:%0d\naddress_span:%0d",
"\nalignment:%s\nstride_bytes:%0d\nVLEN:%0d\nLMUL:%0d\ncfg.vector_cfg.vtype.vsew:%0d\n\n"},
"\nstride_bytes:%0d\nVLEN:%0d\nLMUL:%0d\ncfg.vector_cfg.vtype.vsew:%0d\n\n"},
max_load_store_addr, data_page_id, data_page[data_page_id].size_in_bytes, ss,
alignment.name(), stride_bytes(), VLEN,
cfg.vector_cfg.vtype.vlmul, cfg.vector_cfg.vtype.vsew))
stride_bytes(), VLEN, cfg.vector_cfg.vtype.vlmul, cfg.vector_cfg.vtype.vsew))
end
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base, base >= 0; base <= max_load_store_addr;)
if (!cfg.enable_unaligned_load_store) begin
base &= ~get_addr_alignment_mask(stride_bytes());
end
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base, base inside {[0 : max_load_store_addr]};
base % eew == 0;)
endfunction
virtual function int address_span();
@ -636,11 +624,7 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
endfunction
virtual function int stride_bytes();
stride_bytes = 0;
if (alignment == B_ALIGNMENT) stride_bytes = 1;
else if (alignment == H_ALIGNMENT) stride_bytes = 2;
else if (alignment == W_ALIGNMENT) stride_bytes = 4;
else if (alignment == E_ALIGNMENT) stride_bytes = cfg.vector_cfg.vtype.vsew;
stride_bytes = eew / 8;
endfunction
// Generate each load/store instruction
@ -651,13 +635,6 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
endfunction
virtual function void build_allowed_instr();
if (alignment == B_ALIGNMENT) add_byte_aligned_vec_load_stores();
else if (alignment == H_ALIGNMENT) add_h_aligned_vec_load_stores();
else if (alignment == W_ALIGNMENT) add_w_aligned_vec_load_stores();
else if (alignment == E_ALIGNMENT) add_element_vec_load_stores();
endfunction
virtual function void add_element_vec_load_stores();
case (address_mode)
UNIT_STRIDED : begin
allowed_instr = {VLE_V, VSE_V, allowed_instr};
@ -678,96 +655,9 @@ class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream;
end
end
INDEXED : begin
allowed_instr = {VLXE_V, VSXE_V, allowed_instr};
allowed_instr = {VLXEI_V, VSXEI_V, VSUXEI_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLXSEGE_V, VSXSEGE_V, allowed_instr};
end
end
endcase
endfunction
virtual function void add_byte_aligned_vec_load_stores();
case (address_mode)
UNIT_STRIDED : begin
allowed_instr = {VLB_V, VSB_V, VLBU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLBFF_V, VLBUFF_V, allowed_instr};
end
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSEGB_V, VSSEGB_V, VLSEGBU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLSEGBFF_V, VLSEGBUFF_V, allowed_instr};
end
end
end
STRIDED : begin
allowed_instr = {VLSB_V, VSSB_V, VLSBU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSSEGB_V, VSSSEGB_V, VLSSEGBU_V, allowed_instr};
end
end
INDEXED : begin
allowed_instr = {VLXB_V, VSXB_V, VLXBU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLXSEGB_V, VSXSEGB_V, VLXSEGBU_V, allowed_instr};
end
end
endcase
endfunction
virtual function void add_h_aligned_vec_load_stores();
case (address_mode)
UNIT_STRIDED : begin
allowed_instr = {VLH_V, VSH_V, VLHU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLHFF_V, VLHUFF_V, allowed_instr};
end
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSEGH_V, VSSEGH_V, VLSEGHU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLSEGHFF_V, VLSEGHUFF_V, allowed_instr};
end
end
end
STRIDED : begin
allowed_instr = {VLSH_V, VSSH_V, VLSHU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSSEGH_V, VSSSEGH_V, VLSSEGHU_V, allowed_instr};
end
end
INDEXED : begin
allowed_instr = {VLXH_V, VSXH_V, VLXHU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLXSEGH_V, VSXSEGH_V, VLXSEGHU_V, allowed_instr};
end
end
endcase
endfunction
virtual function void add_w_aligned_vec_load_stores();
case (address_mode)
UNIT_STRIDED : begin
allowed_instr = {VLW_V, VSW_V, VLWU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLWFF_V, VLWUFF_V, allowed_instr};
end
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSEGW_V, VSSEGW_V, VLSEGWU_V, allowed_instr};
if (cfg.vector_cfg.enable_fault_only_first_load) begin
allowed_instr = {VLSEGWFF_V, VLSEGWUFF_V, allowed_instr};
end
end
end
STRIDED : begin
allowed_instr = {VLSW_V, VSSW_V, VLSWU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLSSEGW_V, VSSSEGW_V, VLSSEGWU_V, allowed_instr};
end
end
INDEXED : begin
allowed_instr = {VLXW_V, VSXW_V, VLXWU_V, allowed_instr};
if (cfg.vector_cfg.enable_zvlsseg) begin
allowed_instr = {VLXSEGW_V, VSXSEGW_V, VLXSEGWU_V, allowed_instr};
allowed_instr = {VLXSEGEI_V, VSXSEGEI_V, VSUXSEGEI_V, allowed_instr};
end
end
endcase

View file

@ -371,6 +371,8 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// End of page table fault handling
instr.push_back("fix_pte_done:");
// Make sure all outstanding memory access is completed
instr.push_back("sfence.vma");
// Randomly decide if run some kernel program before exiting from exception handling
// Use the low 2 bits of x30 to determine whether to skip it or not.
instr.push_back($sformatf("slli x30, x30, %0d", XLEN - 2));
@ -526,6 +528,7 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// If not the end of the kernel space, process the next PTE
$sformatf("ble x%0d, x%0d, 2b", cfg.gpr[0], cfg.gpr[1])};
end
instr = {instr, "sfence.vma"};
endfunction
// If you want to create custom page table topology, override the below tasks to specify the

View file

@ -77,8 +77,6 @@ class riscv_pmp_cfg extends uvm_object;
pmp_granularity inside {[0 : XLEN + 3]};
}
// TODO(udinator) more address constraints?
// TODO(udinator) move to posts_randomize() if lower performance
constraint xwr_c {
foreach (pmp_cfg[i]) {
!(pmp_cfg[i].w && !pmp_cfg[i].r);
@ -438,7 +436,8 @@ class riscv_pmp_cfg extends uvm_object;
$sformatf("li x%0d, 3", scratch_reg[0]),
$sformatf("beq x%0d, x%0d, 27f", scratch_reg[4], scratch_reg[0]),
// Error check, if no address modes match, something has gone wrong
$sformatf("j test_done"),
$sformatf("la x%0d, test_done", scratch_reg[0]),
$sformatf("jalr x0, x%0d, 0", scratch_reg[0]),
/////////////////////////////////////////////////////////////////
// increment loop counter and branch back to beginning of loop //
/////////////////////////////////////////////////////////////////
@ -459,7 +458,8 @@ class riscv_pmp_cfg extends uvm_object;
// We must immediately jump to <test_done> since the CPU is taking a PMP exception,
// but this routine is unable to find a matching PMP region for the faulting access -
// there is a bug somewhere.
$sformatf("19: j test_done")
$sformatf("19: la x%0d, test_done", scratch_reg[0]),
$sformatf("jalr x0, x%0d, 0", scratch_reg[0])
};
/////////////////////////////////////////////////
@ -495,8 +495,8 @@ class riscv_pmp_cfg extends uvm_object;
// <test_done>, otherwise modify access bits and return
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
$sformatf("beqz x%0d, 24f", scratch_reg[4]),
$sformatf("j test_done"),
// TODO : update with correct label
$sformatf("la x%0d, test_done", scratch_reg[0]),
$sformatf("jalr x0, x%0d, 0", scratch_reg[0]),
$sformatf("24: j 29f")
};
@ -515,8 +515,8 @@ class riscv_pmp_cfg extends uvm_object;
// entry is locked, otherwise modify access bits
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
$sformatf("beqz x%0d, 26f", scratch_reg[4]),
$sformatf("j test_done"),
// TODO : update with correct label
$sformatf("la x%0d, test_done", scratch_reg[0]),
$sformatf("jalr x0, x%0d, 0", scratch_reg[0]),
$sformatf("26: j 29f")
};
@ -540,8 +540,8 @@ class riscv_pmp_cfg extends uvm_object;
// the entry is locked, otherwise modify access bits
$sformatf("andi x%0d, x%0d, 128", scratch_reg[4], scratch_reg[3]),
$sformatf("beqz x%0d, 29f", scratch_reg[4]),
$sformatf("j test_done"),
// TODO : update with correct label
$sformatf("la x%0d, test_done", scratch_reg[0]),
$sformatf("jalr x0, x%0d, 0", scratch_reg[0]),
$sformatf("28: j 29f")
};

View file

@ -67,7 +67,7 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
add_field("WPRI1", 1, WPRI);
add_field("MPIE", 1, WARL);
add_field("SPP", 1, WLRL);
add_field("WPRI2", 2, WPRI);
add_field("VS", 2, WARL);
add_field("MPP", 2, WLRL);
add_field("FS", 2, WARL);
add_field("XS", 2, WARL);
@ -77,11 +77,10 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
add_field("TVM", 1, WARL);
add_field("TW", 1, WARL);
add_field("TSR", 1, WARL);
add_field("VS", 2, WARL);
if(XLEN == 32) begin
add_field("WPRI3", 6, WPRI);
add_field("WPRI3", 8, WPRI);
end else begin
add_field("WPRI3", 7, WPRI);
add_field("WPRI3", 9, WPRI);
add_field("UXL", 2, WARL);
add_field("SXL", 2, WARL);
add_field("WPRI4", XLEN - 37, WPRI);

View file

@ -24,6 +24,9 @@ class riscv_vector_cfg extends uvm_object;
rand bit vxsat;
riscv_vreg_t reserved_vregs[$];
// Allowed effective element width based on the LMUL setting
int unsigned legal_eew[$];
// Allow only vector instructions from the random sequences
rand bit only_vec_instr;
constraint only_vec_instr_c {soft only_vec_instr == 0;}
@ -80,10 +83,10 @@ class riscv_vector_cfg extends uvm_object;
vtype.vlmul inside {1, 2, 4, 8};
vtype.vlmul <= MAX_LMUL;
if (vec_narrowing_widening) {
vtype.vlmul < 8;
(vtype.vlmul < 8) || (vtype.fractional_lmul == 1'b1);
}
if (vec_quad_widening) {
vtype.vlmul < 4;
(vtype.vlmul < 4) || (vtype.fractional_lmul == 1'b1);
}
}
@ -110,6 +113,8 @@ class riscv_vector_cfg extends uvm_object;
`uvm_field_int(vtype.vediv, UVM_DEFAULT)
`uvm_field_int(vtype.vsew, UVM_DEFAULT)
`uvm_field_int(vtype.vlmul, UVM_DEFAULT)
`uvm_field_int(vtype.fractional_lmul, UVM_DEFAULT)
`uvm_field_queue_int(legal_eew, UVM_DEFAULT)
`uvm_field_int(vl, UVM_DEFAULT)
`uvm_field_int(vstart, UVM_DEFAULT)
`uvm_field_enum(vxrm_t,vxrm, UVM_DEFAULT)
@ -128,4 +133,24 @@ class riscv_vector_cfg extends uvm_object;
end
endfunction : new
function void post_randomize();
real temp_eew;
legal_eew = {};
// Section 7.3 Vector loads and stores have the EEW encoded directly in the instruction.
// EMUL is calculated as EMUL =(EEW/SEW)*LMUL. If the EMUL would be out of range
// (EMUL>8 or EMUL<1/8), an illegal instruction exceptionis raised.
// EEW = SEW * EMUL / LMUL
for (real emul = 0.125; emul <= 8; emul = emul * 2) begin
if (vtype.fractional_lmul == 0) begin
temp_eew = real'(vtype.vsew) * emul / real'(vtype.vlmul);
end else begin
temp_eew = real'(vtype.vsew) * emul * real'(vtype.vlmul);
end
if (temp_eew inside {[8:1024]}) begin
legal_eew.push_back(int'(temp_eew));
end
`uvm_info(`gfn, $sformatf("Checking emul: %.2f", emul), UVM_LOW)
end
endfunction : post_randomize
endclass : riscv_vector_cfg

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -0,0 +1,22 @@
# riscOVPsim configuration file converted from YAML
--variant RV32I
--override riscvOVPsim/cpu/add_Extensions=MC
--override riscvOVPsim/cpu/misa_MXL=1
--override riscvOVPsim/cpu/misa_MXL_mask=0x0 # 0
--override riscvOVPsim/cpu/misa_Extensions_mask=0x0 # 0
--override riscvOVPsim/cpu/unaligned=T
--override riscvOVPsim/cpu/mtvec_mask=0x0 # 0
--override riscvOVPsim/cpu/user_version=2.3
--override riscvOVPsim/cpu/priv_version=1.11
--override riscvOVPsim/cpu/mvendorid=0
--override riscvOVPsim/cpu/marchid=0
--override riscvOVPsim/cpu/mimpid=0
--override riscvOVPsim/cpu/mhartid=0
--override riscvOVPsim/cpu/cycle_undefined=F
--override riscvOVPsim/cpu/instret_undefined=F
--override riscvOVPsim/cpu/time_undefined=T
--override riscvOVPsim/cpu/reset_address=0x80000000
--override riscvOVPsim/cpu/simulateexceptions=T
--override riscvOVPsim/cpu/defaultsemihost=F
--override riscvOVPsim/cpu/wfi_is_nop=T
--exitonsymbol _exit

View file

@ -0,0 +1,146 @@
/*
* 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.
*/
//-----------------------------------------------------------------------------
// Processor feature configuration
//-----------------------------------------------------------------------------
// XLEN
parameter int XLEN = 32;
// Parameter for SATP mode, set to BARE if address translation is not supported
parameter satp_mode_t SATP_MODE = SV32;
// Supported Privileged mode
privileged_mode_t supported_privileged_mode[] = {MACHINE_MODE, USER_MODE};
// Unsupported instructions
riscv_instr_name_t unsupported_instr[];
// ISA supported by the processor
riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C};
// Interrupt mode support
mtvec_mode_t supported_interrupt_mode[$] = {DIRECT, VECTORED};
// The number of interrupt vectors to be generated, only used if VECTORED interrupt mode is
// supported
int max_interrupt_vector_num = 16;
// Physical memory protection support
bit support_pmp = 0;
// Debug mode support
bit support_debug_mode = 0;
// Support delegate trap to user mode
bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 0;
// Support unaligned load/store
bit support_unaligned_load_store = 1'b1;
// GPR setting
parameter int NUM_FLOAT_GPR = 32;
parameter int NUM_GPR = 32;
parameter int NUM_VEC_GPR = 32;
// ----------------------------------------------------------------------------
// Vector extension configuration
// ----------------------------------------------------------------------------
// Parameter for vector extension
parameter int VECTOR_EXTENSION_ENABLE = 0;
parameter int VLEN = 512;
// Maximum size of a single vector element
parameter int ELEN = 32;
// Minimum size of a sub-element, which must be at most 8-bits.
parameter int SELEN = 8;
// Maximum size of a single vector element (encoded in vsew format)
parameter int VELEN = int'($ln(ELEN)/$ln(2)) - 3;
// Maxium LMUL supported by the core
parameter int MAX_LMUL = 8;
// ----------------------------------------------------------------------------
// Multi-harts configuration
// ----------------------------------------------------------------------------
// Number of harts
parameter int NUM_HARTS = 1;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------
// Implemented previlieged CSR list
`ifdef DSIM
privileged_reg_t implemented_csr[] = {
`else
const privileged_reg_t implemented_csr[] = {
`endif
// Machine mode mode CSR
MVENDORID, // Vendor ID
MARCHID, // Architecture ID
MIMPID, // Implementation ID
MHARTID, // Hardware thread ID
MSTATUS, // Machine status
MISA, // ISA and extensions
MIE, // Machine interrupt-enable register
MTVEC, // Machine trap-handler base address
MCOUNTEREN, // Machine counter enable
MSCRATCH, // Scratch register for machine trap handlers
MEPC, // Machine exception program counter
MCAUSE, // Machine trap cause
MTVAL, // Machine bad address or instruction
MIP // Machine interrupt pending
};
// Implementation-specific custom CSRs
bit [11:0] custom_csr[] = {
};
// ----------------------------------------------------------------------------
// Supported interrupt/exception setting, used for functional coverage
// ----------------------------------------------------------------------------
`ifdef DSIM
interrupt_cause_t implemented_interrupt[] = {
`else
const interrupt_cause_t implemented_interrupt[] = {
`endif
M_SOFTWARE_INTR,
M_TIMER_INTR,
M_EXTERNAL_INTR
};
`ifdef DSIM
exception_cause_t implemented_exception[] = {
`else
const exception_cause_t implemented_exception[] = {
`endif
INSTRUCTION_ACCESS_FAULT,
ILLEGAL_INSTRUCTION,
BREAKPOINT,
LOAD_ADDRESS_MISALIGNED,
LOAD_ACCESS_FAULT,
ECALL_MMODE
};

View file

@ -0,0 +1,44 @@
# Copyright 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 test list format
# --------------------------------------------------------------------------------
# test : Assembly test name
# description : Description of this test
# gen_opts : Instruction generator options
# iterations : Number of iterations of this test
# no_iss : Enable/disable ISS simulator (Optional)
# gen_test : Test name used by the instruction generator
# asm_tests : Path to directed, hand-coded assembly test file or directory
# rtl_test : RTL simulation test name
# cmp_opts : Compile options passed to the instruction generator
# sim_opts : Simulation options passed to the instruction generator
# no_post_compare : Enable/disable comparison of trace log and ISS log (Optional)
# compare_opts : Options for the RTL & ISS trace comparison
# gcc_opts : gcc compile options
# --------------------------------------------------------------------------------
- import: <riscv_dv_root>/target/rv32imc/testlist.yaml
- test: riscv_u_mode_rand_test
description: >
Random previliged mode test
iterations: 2
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=10000
+num_of_sub_program=5
+boot_mode=u
rtl_test: core_base_test

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -141,11 +141,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -7,7 +7,7 @@
--override riscvOVPsim/cpu/mtvec_mask=0x0 # 0
--override riscvOVPsim/cpu/user_version=2.3
--override riscvOVPsim/cpu/priv_version=1.11
--override riscvOVPsim/cpu/vector_version=0.8
--override riscvOVPsim/cpu/vector_version=0.9
--override riscvOVPsim/cpu/mvendorid=0
--override riscvOVPsim/cpu/marchid=0
--override riscvOVPsim/cpu/mimpid=0

View file

@ -140,11 +140,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -29,7 +29,80 @@
# gcc_opts : gcc compile options
# --------------------------------------------------------------------------------
- import: <riscv_dv_root>/target/rv64gc/testlist.yaml
- import: <riscv_dv_root>/target/rv64imc/testlist.yaml
- test: riscv_invalid_csr_test
description: >
Boot core into random privileged mode and generate csr accesses to invalid CSRs (at a higher priv mode)
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+instr_cnt=6000
+num_of_sub_program=0
+enable_access_invalid_csr_level=1
rtl_test: core_invalid_csr_test
sim_opts: >
+require_signature_addr=1
- test: riscv_amo_test
description: >
RISC-V atomic instruction extension test
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+instr_cnt=5000
+num_of_sub_program=5
+directed_instr_0=riscv_lr_sc_instr_stream,10
+directed_instr_1=riscv_amo_instr_stream,10
rtl_test: core_base_test
- test: riscv_floating_point_arithmetic_test
description: >
Enable floating point instructions
gen_opts: >
+instr_cnt=10000
+num_of_sub_program=0
+no_fence=1
+no_data_page=1
+no_branch_jump=1
+enable_floating_point=1
+boot_mode=m
iterations: 1
gen_test: riscv_instr_base_test
rtl_test: core_base_test
- test: riscv_floating_point_rand_test
description: >
Enable floating point instructions
gen_opts: >
+enable_floating_point=1
+instr_cnt=10000
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,4
+directed_instr_1=riscv_loop_instr,4
+directed_instr_2=riscv_multi_page_load_store_instr_stream,4
+directed_instr_3=riscv_mem_region_stress_test,4
+directed_instr_4=riscv_jal_instr,4
iterations: 1
gen_test: riscv_instr_base_test
rtl_test: core_base_test
- test: riscv_floating_point_mmu_stress_test
description: >
Test with different patterns of load/store instructions, stress test MMU
operations.
iterations: 2
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=5000
+num_of_sub_program=5
+enable_floating_point=1
+directed_instr_0=riscv_load_store_rand_instr_stream,40
+directed_instr_1=riscv_load_store_hazard_instr_stream,40
+directed_instr_2=riscv_multi_page_load_store_instr_stream,10
+directed_instr_3=riscv_mem_region_stress_test,10
rtl_test: core_base_test
- test: riscv_vector_arithmetic_test
description: >

View file

@ -114,11 +114,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -115,11 +115,7 @@ const privileged_reg_t implemented_csr[] = {
};
// Implementation-specific custom CSRs
`ifdef DSIM
bit [11:0] custom_csr[] = {
`else
const bit [11:0] custom_csr[] = {
`endif
};
// ----------------------------------------------------------------------------

View file

@ -42,7 +42,7 @@ class riscv_instr_cov_test extends uvm_test;
bit expect_illegal_instr;
entry_cnt = 0;
instr_cg.reset();
if (uvm_is_match("*illegal*", trace_csv[i])) begin
if (uvm_is_match("*illegal*", trace_csv[i]) || uvm_is_match("*unknown*", trace_csv[i]) ) begin
expect_illegal_instr = 1;
end
`uvm_info(`gfn, $sformatf("Processing CSV trace[%0d]: %s", i, trace_csv[i]), UVM_LOW)

View file

@ -135,3 +135,8 @@
sim:
cmd: >
xrun -R -xmlibdirpath <out> <sim_opts> -svseed <seed> -svrnc rand_struct -nokey
- tool: pyflow
sim:
cmd: >
python3 <cwd>/pygen/pygen_src/test/<test_name>.py <sim_opts>