Update google_riscv-dv to google/riscv-dv@ad6fe56 (#385)

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

* Workaround for dsim compile issue (google/riscv-dv#211) (taoliug)
* Add a --seed_yaml option to rerun a regression with the same seed of
  a prior regression (google/riscv-dv#210) (taoliug)
* Update questa covearge options (google/riscv-dv#209) (taoliug)
* Fix disable_compressed_instr option (google/riscv-dv#205) (taoliug)
* Fix non-compressed instruction test (google/riscv-dv#203) (taoliug)
* Debug single step functionality and config knobs (Udi)
* Fix no_branch_jump option (google/riscv-dv#200) (taoliug)
* Add more functional covergroup (google/riscv-dv#199) (taoliug)
* Allow randomly reserve GPR for TP/SP, improve functional coverage
  (google/riscv-dv#198) (taoliug)
* Allow running the coverage script with LSF (google/riscv-dv#195)
  (taoliug)
* Add support for disable_compressed_instr (google/riscv-dv#194)
  (taoliug)
* Improve coverage collection performance (google/riscv-dv#193)
  (taoliug)
* Signature_addr_reg constraint update (Udi)
* Add a debug mode for functional coverage (google/riscv-dv#191)
  (taoliug)
* Fix typo in README (google/riscv-dv#189) (taoliug)
* Constrain scratch_reg (google/riscv-dv#188) (udinator)
* Update README for the coverage flow (google/riscv-dv#187) (taoliug)
* Add basic privileged CSR cover group (google/riscv-dv#186) (taoliug)
* Fix cover point definition (google/riscv-dv#185) (taoliug)
* Fix ovpsim log compare issue (google/riscv-dv#183) (udinator)
This commit is contained in:
udinator 2019-10-08 14:22:05 -07:00 committed by GitHub
parent 9824342c03
commit 4a1806f16f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 819 additions and 322 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: d3419444ca2fdb499a204587b2d36c6f5c1e0c44
rev: ad6fe565a91445cc3ea3e32119360b57af4f19b2
}
}

View file

@ -1,4 +1,4 @@
# RISCV-DV
## Overview
RISCV-DV is a SV/UVM based open-source instruction generator for RISC-V
processor verification. It currently supports the following features:
@ -104,7 +104,6 @@ python3 run.py --test riscv_arithmetic_basic_test --co
### Privileged CSR Test Generation
The CSR generation script is located at
[scripts/gen_csr_test.py](https://github.com/google/riscv-dv/blob/master/scripts/gen_csr_test.py).
The CSR test code that this script generates will execute every CSR instruction
@ -313,8 +312,10 @@ it with random instructions
## Compile generated programs with GCC
- Install [riscv-gcc](https://github.com/riscv/riscv-gcc) toolchain
- Set environment variable RISCV_GCC to the directory of the RISC-V gcc
- Set environment variable RISCV_GCC to the RISC-V gcc executable
executable. (example: <install_dir>/bin/riscv32-unknown-elf-gcc)
- Set environment variable RISCV_OBJCOPY to RISC-v objcopy executable
executable. (example: <install_dir>/bin/riscv32-unknown-elf-objcopy)
## Run ISS (Instruction Set Simulator) simulation
@ -401,10 +402,65 @@ upstream changes.
You can refer to [riscv-dv extension for ibex](https://github.com/lowRISC/ibex/blob/master/dv/uvm/Makefile#L68) for a working example.
We have plan to open-source the end-to-end environment of other advanced RISC-V
processors. Stay tuned!
## Functional coverage (work in progress)
This flow extracts funcitonal coverage information from the
instruction trace generated by ISS. It's indepdent of the instruction generation
flow and does not require a tracer implmentation in the RTL. You can use this
flow as long as your program can be run with the ISS supported in this flow. The
flow parses the instruction trace log and converts it to a CSV trace format. After
that, a SV test will be run to process the CSV trace files and sample functional
coverage from there.
The functional covergroup is defined in [riscv_instr_cover_group.sv](https://github.com/google/riscv-dv/blob/master/src/riscv_instr_cover_group.sv). It includes below major categories:
- Cover all operands for each instruction
- Hazard conditions
- Corner cases like overflow, underflow, divide by zero
- Aligned/unaligned load/store
- Positive/negative immediate value
- Forward/backward branches, branch hit history
- Hint instruction
- Illegal instruction
- All compressed and non-compressed opcode
- Access to all implemened privieleged CSR
- Exception and interrupt
The functional covergroup is still under active development. Please feel free to
add anything you are interested or file a bug for any feature request.
Before start, please check the you have modified [riscv_core_setting.sv](https://github.com/google/riscv-dv/blob/master/setting/riscv_core_setting.sv) to reflect your processor capabilities. The covergroup is selectively instantiated based on this setting so that you don't need to deal with excluding unrelated coverpoint later. You also need to get spike ISS setup before running this flow.
```
// Process spike simulation log and collect functional coverage
python3 cov.py --dir out/spike_sim
// Get the command reference
python3 cov.py --help
```
The coverage sampling from the CSV could be time consuming if you have a large
number of log to process. You can split them to small batches and run with LSF
in parallel.
```
// Split the run to process 5 CSV at a time, and run with LSF
python3 cov.py --dir out/spike_sim --lsf_cmd "bsub ....." -bz 5
```
There is also a debug mode which allow you randomize the instruction and sample
coverage directly. This is only used to test the new functional coverage
implmentation.
```
// Randomly generate 100000 instructions, split to 20000 instructions per batch
python3 cov.py -d -i 100000 -bz 20000
```
## Supporting model
Please file an issue under this repository for any bug report / integration

View file

@ -67,11 +67,12 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, opts, t
logging.error("Full trace for %s is not supported yet" % iss)
sys.exit(1)
if steps == "all" or re.match("cov", steps):
build_cmd = ("%s python3 run.py --co -o %s --cov -tl %s %s" %
(lsf_cmd, out, testlist, opts))
base_sim_cmd = ("%s python3 run.py --so -o %s --cov -tl %s %s "
build_cmd = ("python3 run.py --co -o %s --cov -tl %s %s" %
(out, testlist, opts))
base_sim_cmd = ("python3 run.py --so -o %s --cov -tl %s %s "
"-tn riscv_instr_cov_test --steps gen --sim_opts \"<trace_csv_opts>\"" %
(lsf_cmd, out, testlist, opts))
(out, testlist, opts))
logging.info("Building the coverage collection framework")
run_cmd(build_cmd)
file_idx = 0
trace_idx = 0
@ -97,6 +98,7 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, opts, t
logging.info("Processing batch %0d/%0d" % (file_idx+1, batch_cnt))
run_cmd(sim_cmd)
else:
sim_cmd += (" --lsf_cmd \"%s\"" % lsf_cmd)
sim_cmd_list.append(sim_cmd)
trace_csv_opts = ""
if lsf_cmd != "":
@ -104,6 +106,54 @@ def collect_cov(log_dir, out, iss, testlist, batch_size, lsf_cmd, steps, opts, t
logging.info("Collecting functional coverage from %0d trace CSV...done" % len(csv_list))
def run_cov_debug_test(out, instr_cnt, testlist, batch_size, opts, lsf_cmd, timeout):
"""Collect functional coverage from the instruction trace
Args:
out : Output directory
instr_cnt : Number of instruction to randomize
test_list : Testlist of the coverage test
batch_size : Number of trace CSV to process per test
lsf_cmd : LSF command used to run the instruction generator
opts : Additional options to the instruction generator
timeout : Timeout limit in seconds
"""
sim_cmd_list = []
logging.info("Building the coverage collection framework")
build_cmd = ("python3 run.py --co -o %s --cov -tl %s %s" %
(out, testlist, opts))
run_cmd(build_cmd)
base_sim_cmd = ("python3 run.py --so -o %s --cov -tl %s %s "
"-tn riscv_instr_cov_debug_test --steps gen "
"--sim_opts \"+num_of_iterations=<instr_cnt>\"" %
(out, testlist, opts))
if batch_size > 0:
batch_cnt = int((instr_cnt+batch_size-1)/batch_size)
logging.info("Batch size: %0d, Batch cnt:%0d" % (batch_size, batch_cnt))
else:
batch_cnt = 1
logging.info("Randomizing %0d instructions in %0d batches", instr_cnt, batch_cnt)
for i in range(batch_cnt):
if batch_size > 0:
if i == batch_cnt - 1:
batch_instr_cnt = instr_cnt - batch_size * (batch_cnt - 1)
else:
batch_instr_cnt = batch_size
else:
batch_instr_cnt = instr_cnt
sim_cmd = base_sim_cmd.replace("<instr_cnt>", str(batch_instr_cnt))
sim_cmd += (" --log_suffix _%d" % i)
if lsf_cmd == "":
logging.info("Running batch %0d/%0d" % (i+1, batch_cnt))
run_cmd(sim_cmd)
else:
sim_cmd += (" --lsf_cmd \"%s\"" % lsf_cmd)
sim_cmd_list.append(sim_cmd)
if lsf_cmd != "":
run_parallel_cmd(sim_cmd_list, timeout)
logging.info("Collecting functional coverage from %0d random instructions...done" % instr_cnt)
def setup_parser():
"""Create a command line parser.
@ -119,6 +169,10 @@ def setup_parser():
help="Directory of ISS log")
parser.add_argument("-bz", "--batch_size", dest="batch_size", type=int, default=0,
help="Number of CSV to process per run")
parser.add_argument("-d", "--debug_mode", dest="debug_mode", action="store_true",
help="Debug mode, randomize and sample the coverage directly")
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",
@ -133,6 +187,7 @@ def setup_parser():
help="LSF command. Run in local sequentially if lsf \
command is not specified")
parser.set_defaults(verbose=False)
parser.set_defaults(debug_mode=False)
return parser
def main():
@ -142,6 +197,9 @@ def main():
cwd = os.path.dirname(os.path.realpath(__file__))
setup_logging(args.verbose)
if args.verbose:
args.opts += "-v"
if not args.testlist:
args.testlist = cwd + "/yaml/cov_testlist.yaml"
@ -153,8 +211,12 @@ def main():
subprocess.run(["mkdir", "-p", output_dir])
collect_cov(args.dir, output_dir, args.iss, args.testlist, args.batch_size,
args.lsf_cmd, args.steps, args.opts, args.timeout)
if args.debug_mode:
run_cov_debug_test(output_dir, args.instr_cnt, args.testlist,
args.batch_size, args.opts, args.lsf_cmd, args.timeout)
else:
collect_cov(args.dir, output_dir, args.iss, args.testlist, args.batch_size,
args.lsf_cmd, args.steps, args.opts, args.timeout)
if __name__ == "__main__":
main()

View file

@ -119,7 +119,7 @@ def get_iss_cmd(base_cmd, elf, log):
def gen(test_list, csr_file, end_signature_addr, isa, simulator,
simulator_yaml, output_dir, sim_only, compile_only, lsf_cmd, seed,
cwd, cmp_opts, sim_opts, timeout_s, core_setting_dir, ext_dir, cov,
log_suffix, batch_size):
log_suffix, batch_size, seed_yaml):
"""Run the instruction generator
Args:
@ -142,6 +142,7 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
cov : Enable functional coverage
log_suffix : Simulation log file name suffix
batch_size : Number of tests to generate per run
seed_yaml : Seed specification from a prior regression
"""
# Mutually exclusive options between compile_only and sim_only
if compile_only and sim_only:
@ -177,7 +178,12 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
sim_cmd = re.sub("<out>", os.path.abspath(output_dir), sim_cmd)
sim_cmd = re.sub("<cwd>", cwd, sim_cmd)
sim_cmd = re.sub("<sim_opts>", sim_opts, sim_cmd)
if seed_yaml:
rerun_seed = read_yaml(seed_yaml)
else:
rerun_seed = {}
logging.info("Running RISC-V instruction generator")
sim_seed = {}
for test in test_list:
iterations = test['iterations']
logging.info("Generating %d %s" % (iterations, test['test']))
@ -204,7 +210,11 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
batch_cnt = 1
logging.info("Running %s with %0d batches" % (test['test'], batch_cnt))
for i in range(0, batch_cnt):
rand_seed = get_seed(seed)
test_id = '%0s_%0d' % (test['test'], i)
if test_id in rerun_seed:
rand_seed = rerun_seed[test_id]
else:
rand_seed = get_seed(seed)
if i < batch_cnt - 1:
test_cnt = batch_size
else:
@ -216,16 +226,20 @@ def gen(test_list, csr_file, end_signature_addr, isa, simulator,
(" +asm_file_name=%s/asm_tests/%s " % (output_dir, test['test'])) + \
(" -l %s/sim_%s_%d%s.log " % (output_dir, test['test'], i, log_suffix))
cmd = re.sub("<seed>", str(rand_seed), cmd)
sim_seed[test_id] = str(rand_seed)
if "gen_opts" in test:
cmd += test['gen_opts']
if not re.search("c", isa):
cmd += "+disable_comparessed_instr=1";
cmd += "+disable_compressed_instr=1 ";
if lsf_cmd:
cmd_list.append(cmd)
else:
logging.info("Running %s, batch %0d/%0d, test_cnt:%0d" %
(test['test'], i+1, batch_cnt, test_cnt))
run_cmd(cmd, timeout_s)
if sim_seed:
with open(('%s/seed.yaml' % os.path.abspath(output_dir)) , 'w') as outfile:
yaml.dump(sim_seed, outfile, default_flow_style=False)
if lsf_cmd:
run_parallel_cmd(cmd_list, timeout_s)
@ -443,6 +457,9 @@ def setup_parser():
help="RTL simulator setting YAML")
parser.add_argument("--csr_yaml", type=str, default="",
help="CSR description file")
parser.add_argument("--seed_yaml", type=str, default="",
help="Rerun the generator with the seed specification \
from a prior regression")
parser.add_argument("-cs", "--core_setting_dir", type=str, default="",
help="Path for the riscv_core_setting.sv")
parser.add_argument("-ext", "--user_extension_dir", type=str, default="",
@ -503,25 +520,26 @@ def main():
sys.exit("Cannot find %s in %s" % (args.test, args.testlist))
# Run instruction generator
if args.steps == "all" or re.match("gen", args.steps):
if args.steps == "all" or re.match(".*gen.*", args.steps):
gen(matched_list, args.csr_yaml, args.end_signature_addr, args.isa,
args.simulator, args.simulator_yaml, output_dir, args.so,
args.co, args.lsf_cmd, args.seed, cwd, args.cmp_opts,
args.sim_opts, args.gen_timeout, args.core_setting_dir,
args.user_extension_dir, args.cov, args.log_suffix, args.batch_size)
args.user_extension_dir, args.cov, args.log_suffix, args.batch_size,
args.seed_yaml)
if not args.co:
# Compile the assembly program to ELF, convert to plain binary
if args.steps == "all" or re.match("gcc_compile", args.steps):
if args.steps == "all" or re.match(".*gcc_compile.*", args.steps):
gcc_compile(matched_list, output_dir, args.isa, args.mabi, args.gcc_opts)
# Run ISS simulation
if args.steps == "all" or re.match("iss_sim", args.steps):
if args.steps == "all" or re.match(".*iss_sim.*", args.steps):
iss_sim(matched_list, output_dir, args.iss, args.iss_yaml,
args.isa, args.iss_timeout)
# Compare ISS simulation result
if args.steps == "all" or re.match("iss_cmp", args.steps):
if args.steps == "all" or re.match(".*iss_cmp.*", args.steps):
iss_cmp(matched_list, args.iss, output_dir, args.isa)
if __name__ == "__main__":

View file

@ -120,11 +120,11 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
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 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
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
@ -166,12 +166,22 @@ def compare_trace_csv(csv1, csv2, name1, name2, log,
trace_2_index += 1
# Check the final value match between the two traces
for gpr in gpr_trace_1:
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])))
elif int(gpr_trace_1[gpr][-1].rd_val, 16) != \
int(gpr_trace_2[gpr][-1].rd_val, 16):
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 = RiscvInstructiontTraceEntry()
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")

View file

@ -78,6 +78,8 @@ def process_spike_sim_log(spike_log, csv, full_trace = 0):
nextline = f.readline()
if nextline != "":
if ILLE_RE.search(nextline):
if full_trace:
trace_csv.write_trace_entry(rv_instr_trace)
continue
m = RD_RE.search(nextline)
if m:

View file

@ -53,7 +53,11 @@ bit support_sfence = 1;
// ----------------------------------------------------------------------------
// Implemented previlieged CSR list
`ifdef DSIM
privileged_reg_t implemented_csr[] = {
`else
parameter privileged_reg_t implemented_csr[] = {
`endif
// User mode CSR
USTATUS, // User status
UIE, // User interrupt-enable register
@ -94,3 +98,44 @@ parameter privileged_reg_t implemented_csr[] = {
MTVAL, // Machine bad address or instruction
MIP // Machine interrupt pending
};
// ----------------------------------------------------------------------------
// Supported interrupt/exception setting, used for functional coverage
// ----------------------------------------------------------------------------
`ifdef DSIM
interrupt_cause_t implemented_interrupt[] = {
`else
parameter interrupt_cause_t implemented_interrupt[] = {
`endif
U_SOFTWARE_INTR,
S_SOFTWARE_INTR,
M_SOFTWARE_INTR,
U_TIMER_INTR,
S_TIMER_INTR,
M_TIMER_INTR,
U_EXTERNAL_INTR,
S_EXTERNAL_INTR,
M_EXTERNAL_INTR
};
`ifdef DSIM
exception_cause_t implemented_exception[] = {
`else
parameter exception_cause_t implemented_exception[] = {
`endif
INSTRUCTION_ADDRESS_MISALIGNED,
INSTRUCTION_ACCESS_FAULT,
ILLEGAL_INSTRUCTION,
BREAKPOINT,
LOAD_ADDRESS_MISALIGNED,
LOAD_ACCESS_FAULT,
STORE_AMO_ADDRESS_MISALIGNED,
STORE_AMO_ACCESS_FAULT,
ECALL_UMODE,
ECALL_SMODE,
ECALL_MMODE,
INSTRUCTION_PAGE_FAULT,
LOAD_PAGE_FAULT,
STORE_AMO_PAGE_FAULT
};

View file

@ -31,7 +31,6 @@ class riscv_asm_program_gen extends uvm_object;
riscv_instr_sequence sub_program[];
riscv_instr_sequence debug_program;
riscv_instr_sequence debug_sub_program[];
string instr_binary[$];
// Kernel programs
// These programs are called in the interrupt/exception handling routine based on the privileged
// mode settings. For example, when the interrupt/exception is delegated to S-mode, if both SUM
@ -90,7 +89,7 @@ class riscv_asm_program_gen extends uvm_object;
.instr_stream(main_program.directed_instr));
main_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(main_program)
main_program.gen_instr(1);
main_program.gen_instr(.is_main_program(1), .no_branch(cfg.no_branch_jump));
// Setup jump instruction among main program and sub programs
gen_callstack(main_program, sub_program, sub_program_name, cfg.num_of_sub_program);
`uvm_info(`gfn, "Generating callstack...done", UVM_LOW)
@ -104,15 +103,6 @@ class riscv_asm_program_gen extends uvm_object;
// Shuffle the sub programs and insert to the instruction stream
insert_sub_program(sub_program, instr_stream);
`uvm_info(`gfn, "Inserting sub-programs...done", UVM_LOW)
// Reserve some space to copy instruction from data section
if (instr_binary.size() > 0) begin
instr_stream.push_back(".align 2");
instr_stream.push_back("sub_bin:");
instr_stream.push_back({indent, $sformatf(".rept %0d", 2 * instr_binary.size())});
instr_stream.push_back({indent, "nop"});
instr_stream.push_back({indent, ".endr"});
instr_stream.push_back({indent, "ret"});
end
`uvm_info(`gfn, "Main/sub program generation...done", UVM_LOW)
// Program end
gen_program_end();
@ -194,7 +184,7 @@ class riscv_asm_program_gen extends uvm_object;
seq.is_debug_program = 0;
seq.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(seq)
seq.gen_instr(0);
seq.gen_instr(.is_main_program(0), .no_branch(cfg.no_branch_jump));
seq.post_process_instr();
seq.generate_instr_stream();
instr_stream = {instr_stream, seq.instr_string_list};
@ -227,7 +217,7 @@ class riscv_asm_program_gen extends uvm_object;
sub_program[i].label_name = sub_program[i].get_name();
sub_program[i].cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(sub_program[i])
sub_program[i].gen_instr(0);
sub_program[i].gen_instr(.is_main_program(0), .no_branch(cfg.no_branch_jump));
sub_program_name.push_back(sub_program[i].label_name);
end
end
@ -282,6 +272,9 @@ class riscv_asm_program_gen extends uvm_object;
instr_stream.push_back(".include \"user_define.h\"");
instr_stream.push_back(".globl _start");
instr_stream.push_back(".section .text");
if (cfg.disable_compressed_instr) begin
instr_stream.push_back(".option norvc;");
end
instr_stream.push_back("_start:");
endfunction
@ -342,20 +335,9 @@ class riscv_asm_program_gen extends uvm_object;
str = format_string("_init:", LABEL_STR_LEN);
instr_stream.push_back(str);
// Init stack pointer to point to the end of the user stack
str = {indent, "la sp, _user_stack_end"};
str = {indent, $sformatf("la x%0d, _user_stack_end", cfg.sp)};
instr_stream.push_back(str);
core_is_initialized();
// Copy the instruction from data section to instruction section
if (instr_binary.size() > 0) begin
instr_stream.push_back({indent, "la x31, instr_bin"});
instr_stream.push_back({indent, "la x30, sub_bin"});
instr_stream.push_back({indent, $sformatf(".rept %0d", instr_binary.size())});
instr_stream.push_back({indent, "lw x29, 0(x31)"});
instr_stream.push_back({indent, "sw x29, 0(x30)"});
instr_stream.push_back({indent, "addi x31, x31, 4"});
instr_stream.push_back({indent, "addi x30, x30, 4"});
instr_stream.push_back({indent, ".endr"});
end
endfunction
// Setup MISA based on supported extensions
@ -378,8 +360,8 @@ class riscv_asm_program_gen extends uvm_object;
if (SUPERVISOR_MODE inside {supported_privileged_mode}) begin
misa[MISA_EXT_S] = 1'b1;
end
instr_stream.push_back({indent, $sformatf("li x15, 0x%0x", misa)});
instr_stream.push_back({indent, "csrw misa, x15"});
instr_stream.push_back({indent, $sformatf("li x%0d, 0x%0x",cfg.gpr[0], misa)});
instr_stream.push_back({indent, $sformatf("csrw misa, x%0d",cfg.gpr[0])});
endfunction
// Write to the signature_addr with values to indicate to the core testbench
@ -435,17 +417,15 @@ class riscv_asm_program_gen extends uvm_object;
// possible to compare the GPR value after each instruction execution.
virtual function void gen_register_dump();
string str;
riscv_reg_t base_address_reg;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(base_address_reg, base_address_reg != ZERO;)
// Load base address
str = {indent, $sformatf("la x%0d, _start", base_address_reg)};
str = {indent, $sformatf("la x%0d, _start", cfg.gpr[0])};
instr_stream.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), base_address_reg)};
str = {indent, $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), base_address_reg)};
str = {indent, $sformatf("sw x%0d, %0d(x%0d)", i, i*(XLEN/8), cfg.gpr[0])};
end
instr_stream.push_back(str);
end
@ -458,7 +438,7 @@ class riscv_asm_program_gen extends uvm_object;
virtual function void pre_enter_privileged_mode();
string instr[];
// Setup kerenal stack pointer
gen_section("kernel_sp", {"la tp, _kernel_stack_end"});
gen_section("kernel_sp", {$sformatf("la x%0d, _kernel_stack_end", cfg.tp)});
// Setup interrupt and exception delegation
if(!cfg.no_delegation && (cfg.init_privileged_mode != MACHINE_MODE)) begin
gen_delegation();
@ -516,18 +496,18 @@ class riscv_asm_program_gen extends uvm_object;
virtual function void setup_epc();
string instr[];
string mode_name;
instr = {"la x10, _init"};
instr = {$sformatf("la x%0d, _init", cfg.gpr[0])};
if(cfg.virtual_addr_translation_on) begin
// For supervisor and user mode, use virtual address instead of physical address.
// Virtual address starts from address 0x0, here only the lower 12 bits are kept
// as virtual address offset.
instr = {instr,
$sformatf("slli x10, x10, %0d", XLEN - 12),
$sformatf("srli x10, x10, %0d", XLEN - 12)};
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN - 12),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN - 12)};
end
mode_name = cfg.init_privileged_mode.name();
instr = {instr,
"csrw mepc, x10",
$sformatf("csrw mepc, x%0d", cfg.gpr[0]),
$sformatf("j init_%0s", mode_name.tolower())
};
gen_section("mepc_setup", instr);
@ -563,8 +543,8 @@ class riscv_asm_program_gen extends uvm_object;
deleg_val = deleg_val | (1 << int'(cause));
end
end
instr = {$sformatf("li a0, 0x%0x", deleg_val),
$sformatf("csrw 0x%0x, a0 # %0s", edeleg, edeleg.name())};
instr = {$sformatf("li x%0d, 0x%0x", cfg.gpr[0], deleg_val),
$sformatf("csrw 0x%0x, x%0d # %0s", edeleg, cfg.gpr[0], edeleg.name())};
// Interrupt delegation setup
deleg_val = '0;
foreach(ideleg_enable[cause]) begin
@ -573,8 +553,8 @@ class riscv_asm_program_gen extends uvm_object;
end
end
instr = {instr,
$sformatf("li a0, 0x%0x", deleg_val),
$sformatf("csrw 0x%0x, a0 # %0s", ideleg, ideleg.name())};
$sformatf("li x%0d, 0x%0x", cfg.gpr[0], deleg_val),
$sformatf("csrw 0x%0x, x%0d # %0s", ideleg, cfg.gpr[0], ideleg.name())};
section_name = edeleg.name();
section_name = section_name.tolower();
gen_section($sformatf("%0s_setup", section_name), instr);
@ -596,17 +576,18 @@ class riscv_asm_program_gen extends uvm_object;
!riscv_instr_pkg::support_umode_trap) continue;
if (riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
tvec_name = trap_vec_reg.name();
instr = {instr, $sformatf("la a0, %0s_handler", tvec_name.tolower())};
instr = {instr, $sformatf("la x%0d, %0s_handler", cfg.gpr[0], tvec_name.tolower())};
if (SATP_MODE != BARE && riscv_instr_pkg::supported_privileged_mode[i] != MACHINE_MODE) begin
// For supervisor and user mode, use virtual address instead of physical address.
// Virtual address starts from address 0x0, here only the lower 20 bits are kept
// as virtual address offset.
instr = {instr,
$sformatf("slli a0, a0, %0d", XLEN - 20),
$sformatf("srli a0, a0, %0d", XLEN - 20)};
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN - 20),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN - 20)};
end
instr = {instr, $sformatf("ori a0, a0, %0d", cfg.mtvec_mode)};
instr = {instr, $sformatf("csrw 0x%0x, a0 # %0s", trap_vec_reg, trap_vec_reg.name())};
instr = {instr, $sformatf("ori x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], cfg.mtvec_mode)};
instr = {instr, $sformatf("csrw 0x%0x, x%0d # %0s",
trap_vec_reg, cfg.gpr[0], trap_vec_reg.name())};
end
gen_section("trap_vec_init", instr);
endfunction
@ -670,19 +651,19 @@ class riscv_asm_program_gen extends uvm_object;
end else begin
// Push user mode GPR to kernel stack before executing exception handling, this is to avoid
// exception handling routine modify user program state unexpectedly
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, instr);
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) begin
instr = {instr, $sformatf("csrr x15, 0x%0x # %0s", status, status.name())};
instr = {instr, $sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], status, status.name())};
end
instr = {instr,
// Use scratch CSR to save a GPR value
// Check if the exception is caused by an interrupt, if yes, jump to interrupt handler
// Interrupt is indicated by xCause[XLEN-1]
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
$sformatf("srli a1, a1, %0d", XLEN-1),
$sformatf("bne a1, x0, %0smode_intr_handler", mode)};
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], cause, cause.name()),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN-1),
$sformatf("bne x%0d, x0, %0smode_intr_handler", cfg.gpr[0], mode)};
end
// The trap handler will occupy one 4KB page, it will be allocated one entry in the page table
// with a specific privileged mode.
@ -692,43 +673,43 @@ class riscv_asm_program_gen extends uvm_object;
// Exception handler
instr = {};
if (cfg.mtvec_mode == VECTORED) begin
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, instr);
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
end
gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION);
instr = {instr,
// The trap is caused by an exception, read back xCAUSE, xEPC to see if these
// CSR values are set properly. The checking is done by comparing against the log
// generated by ISA simulator (spike).
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
$sformatf("csrr x31, 0x%0x # %0s", epc, epc.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], epc, epc.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], cause, cause.name()),
// Breakpoint
$sformatf("li a2, 0x%0x # BREAKPOINT", BREAKPOINT),
"beq a1, a2, ebreak_handler",
$sformatf("li x%0d, 0x%0x # BREAKPOINT", cfg.gpr[1], BREAKPOINT),
$sformatf("beq x%0d, x%0d, ebreak_handler", cfg.gpr[0], cfg.gpr[1]),
// Check if it's an ECALL exception. Jump to ECALL exception handler
$sformatf("li a2, 0x%0x # ECALL_UMODE", ECALL_UMODE),
"beq a1, a2, ecall_handler",
$sformatf("li a2, 0x%0x # ECALL_SMODE", ECALL_SMODE),
"beq a1, a2, ecall_handler",
$sformatf("li a2, 0x%0x # ECALL_MMODE", ECALL_MMODE),
"beq a1, a2, ecall_handler",
$sformatf("li x%0d, 0x%0x # ECALL_UMODE", cfg.gpr[1], ECALL_UMODE),
$sformatf("beq x%0d, x%0d, ecall_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x # ECALL_SMODE", cfg.gpr[1], ECALL_SMODE),
$sformatf("beq x%0d, x%0d, ecall_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x # ECALL_MMODE", cfg.gpr[1], ECALL_MMODE),
$sformatf("beq x%0d, x%0d, ecall_handler", cfg.gpr[0], cfg.gpr[1]),
// Page table fault or access fault conditions
$sformatf("li a2, 0x%0x", INSTRUCTION_ACCESS_FAULT),
"beq a1, a2, instr_fault_handler",
$sformatf("li a2, 0x%0x", LOAD_ACCESS_FAULT),
"beq a1, a2, load_fault_handler",
$sformatf("li a2, 0x%0x", STORE_AMO_ACCESS_FAULT),
"beq a1, a2, store_fault_handler",
$sformatf("li a2, 0x%0x", INSTRUCTION_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", LOAD_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li a2, 0x%0x", STORE_AMO_PAGE_FAULT),
"beq a1, a2, pt_fault_handler",
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], INSTRUCTION_ACCESS_FAULT),
$sformatf("beq x%0d, x%0d, instr_fault_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], LOAD_ACCESS_FAULT),
$sformatf("beq x%0d, x%0d, load_fault_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], STORE_AMO_ACCESS_FAULT),
$sformatf("beq x%0d, x%0d, store_fault_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], INSTRUCTION_PAGE_FAULT),
$sformatf("beq x%0d, x%0d, pt_fault_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], LOAD_PAGE_FAULT),
$sformatf("beq x%0d, x%0d, pt_fault_handler", cfg.gpr[0], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[1], STORE_AMO_PAGE_FAULT),
$sformatf("beq x%0d, x%0d, pt_fault_handler", cfg.gpr[0], cfg.gpr[1]),
// Illegal instruction exception
$sformatf("li a2, 0x%0x # ILLEGAL_INSTRUCTION", ILLEGAL_INSTRUCTION),
"beq a1, a2, illegal_instr_handler",
$sformatf("li x%0d, 0x%0x # ILLEGAL_INSTRUCTION", cfg.gpr[1], ILLEGAL_INSTRUCTION),
$sformatf("beq x%0d, x%0d, illegal_instr_handler", cfg.gpr[0], cfg.gpr[1]),
// Skip checking tval for illegal instruction as it's implementation specific
$sformatf("csrr x30, 0x%0x # %0s", tval, tval.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[1], tval, tval.name()),
"1: jal x1, test_done "
};
gen_section($sformatf("%0smode_exception_handler", mode), instr);
@ -754,16 +735,18 @@ class riscv_asm_program_gen extends uvm_object;
for (int i = 1; i < max_interrupt_vector_num; i++) begin
instr.push_back($sformatf("j %0smode_intr_vector_%0d", mode, i));
end
instr = {instr, ".option rvc;"};
if (!cfg.disable_compressed_instr) begin
instr = {instr, ".option rvc;"};
end
for (int i = 1; i < max_interrupt_vector_num; i++) begin
string intr_handler[$];
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, intr_handler);
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, intr_handler);
gen_signature_handshake(.instr(intr_handler), .signature_type(CORE_STATUS), .core_status(HANDLING_IRQ));
intr_handler = {intr_handler,
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], cause, cause.name()),
// Terminate the test if xCause[31] != 0 (indicating exception)
$sformatf("srli a1, a1, 0x%0x", XLEN-1),
$sformatf("beqz a1, 1f")};
$sformatf("srli x%0d, x%0d, 0x%0x", cfg.gpr[0], cfg.gpr[0], XLEN-1),
$sformatf("beqz x%0d, 1f", cfg.gpr[0])};
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(status));
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(cause));
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(ie));
@ -803,11 +786,11 @@ class riscv_asm_program_gen extends uvm_object;
gen_signature_handshake(instr, CORE_STATUS, EBREAK_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
"csrr x31, mepc",
"addi x31, x31, 4",
"csrw mepc, x31"
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section("ebreak_handler", instr);
endfunction
@ -824,11 +807,11 @@ class riscv_asm_program_gen extends uvm_object;
gen_signature_handshake(instr, CORE_STATUS, ILLEGAL_INSTR_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
"csrr x31, mepc",
"addi x31, x31, 4",
"csrw mepc, x31"
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section("illegal_instr_handler", instr);
endfunction
@ -838,7 +821,7 @@ class riscv_asm_program_gen extends uvm_object;
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, INSTR_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section("instr_fault_handler", instr);
endfunction
@ -848,7 +831,7 @@ class riscv_asm_program_gen extends uvm_object;
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section("load_fault_handler", instr);
endfunction
@ -858,7 +841,7 @@ class riscv_asm_program_gen extends uvm_object;
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, STORE_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section("store_fault_handler", instr);
endfunction
@ -948,15 +931,17 @@ class riscv_asm_program_gen extends uvm_object;
// The value of these CSR are checked by comparing with spike simulation result.
interrupt_handler_instr = {
interrupt_handler_instr,
$sformatf("csrr a1, 0x%0x # %0s;", status, status.name()),
$sformatf("csrr a1, 0x%0x # %0s;", ie, ie.name()),
$sformatf("csrr a1, 0x%0x # %0s;", ip, ip.name()),
$sformatf("csrr x%0d, 0x%0x # %0s;", cfg.gpr[0], status, status.name()),
$sformatf("csrr x%0d, 0x%0x # %0s;", cfg.gpr[0], ie, ie.name()),
$sformatf("csrr x%0d, 0x%0x # %0s;", cfg.gpr[0], ip, ip.name()),
// Clean all the pending interrupt
$sformatf("csrrc a1, 0x%0x, a1 # %0s;", ip, ip.name())
$sformatf("csrrc x%0d, 0x%0x, x%0d # %0s;",
cfg.gpr[0], ip, cfg.gpr[0], ip.name())
};
gen_plic_section(interrupt_handler_instr);
// Restore user mode GPR value from kernel stack before return
pop_gpr_from_kernel_stack(status, scratch, cfg.mstatus_mprv, interrupt_handler_instr);
pop_gpr_from_kernel_stack(status, scratch, cfg.mstatus_mprv,
cfg.sp, cfg.tp, interrupt_handler_instr);
interrupt_handler_instr = {interrupt_handler_instr,
$sformatf("%0sret;", mode_prefix)
};
@ -1021,29 +1006,29 @@ class riscv_asm_program_gen extends uvm_object;
string addr_label = "");
if (cfg.require_signature_addr) begin
string str[$];
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_addr_reg, cfg.signature_addr)};
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[1], cfg.signature_addr)};
instr = {instr, str};
case (signature_type)
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of CORE_STATUS, and the upper
// XLEN-8 bits contain the core_status_t data.
CORE_STATUS: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, core_status),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], core_status),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
instr = {instr, str};
end
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of TEST_RESULT, and the upper
// XLEN-8 bits contain the test_result_t data.
TEST_RESULT: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, test_result),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], test_result),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
instr = {instr, str};
end
// The first write to the signature address contains just the
@ -1052,11 +1037,11 @@ class riscv_asm_program_gen extends uvm_object;
// each writing the data contained in one GPR, starting from x0 as the
// first write, and ending with x31 as the 32nd write.
WRITE_GPR: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
instr = {instr, str};
for(int i = 0; i < 32; i++) begin
str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg.signature_addr_reg)};
str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg.gpr[1])};
instr = {instr, str};
end
end
@ -1066,13 +1051,13 @@ class riscv_asm_program_gen extends uvm_object;
// It is followed by a second write to the signature address,
// containing the data stored in the specified CSR.
WRITE_CSR: begin
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, csr),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg),
$sformatf("csrr x%0d, 0x%0h", cfg.signature_data_reg, csr),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
str = {$sformatf("li x%0d, 0x%0h", cfg.gpr[0], csr),
$sformatf("slli x%0d, x%0d, 8", cfg.gpr[0], cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.gpr[0],
cfg.gpr[0], signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1]),
$sformatf("csrr x%0d, 0x%0h", cfg.gpr[0], csr),
$sformatf("sw x%0d, 0(x%0d)", cfg.gpr[0], cfg.gpr[1])};
instr = {instr, str};
end
default: begin
@ -1159,6 +1144,7 @@ class riscv_asm_program_gen extends uvm_object;
//---------------------------------------------------------------------------------------
// Generate the debug rom, and any related programs
// TODO - refactor such that debug_rom is generated by a separate class
//---------------------------------------------------------------------------------------
// Generate the program in the debug ROM
@ -1200,7 +1186,7 @@ class riscv_asm_program_gen extends uvm_object;
instr = {instr, str};
end
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
// Signal that the core entered debug rom only if the rom is actually
// being filled with random instructions to prevent stress tests from
// having to execute unnecessary push/pop of GPRs on the stack ever
@ -1212,17 +1198,56 @@ class riscv_asm_program_gen extends uvm_object;
// TODO(udinator) - randomize the dcsr.ebreak setup
gen_dcsr_ebreak(instr);
end
if (cfg.enable_debug_single_step) begin
// To enable debug single stepping, we must set dcsr.step to 1.
// We will repeat the debug single stepping process a random number
// of times, using a dscratch CSR as the counter, and decrement
// this counter by 1 every time we enter debug mode, until this
// counter reaches 0, at which point we set dcsr.step to 0 until
// the next debug stimulus is asserted.
// Store our designated scratch_reg to dscratch1
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
// Only un-set dcsr.step if it is 1 and the iterations counter
// is at 0 (has finished iterating)
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("andi x%0d, x%0d, 4", cfg.scratch_reg, cfg.scratch_reg),
// If dcsr.step is 0, set to 1 and set the counter
$sformatf("beqz x%0d, 1f", cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
// if the counter is greater than 0, decrement and continue single stepping
$sformatf("bgtz x%0d, 2f", cfg.scratch_reg),
$sformatf("csrc 0x%0x, 0x4", DCSR),
$sformatf("beqz x0, 3f"),
// Set dcsr.step and the num_iterations counter
$sformatf("1: csrs 0x%0x, 0x4", DCSR),
$sformatf("li x%0d, %0d", cfg.scratch_reg, cfg.single_step_iterations),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
$sformatf("beqz x0, 3f"),
// Decrement dscratch counter
$sformatf("2: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0),
$sformatf("addi x%0d, x%0d, -1", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("csrw 0x%0x, x%0d", DSCRATCH0, cfg.scratch_reg),
// Restore scratch_reg value from dscratch1
$sformatf("3: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)
};
instr = {instr, str};
// write dpc to testbench
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DPC));
// write out the counter to the testbench
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DSCRATCH0));
end
// Check dcsr.cause, and update dpc by 0x4 if the cause is ebreak, as
// ebreak will set set dpc to its own address, which will cause an
// infinite loop.
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x1", cfg.signature_data_reg),
$sformatf("bne x%0d, x%0d, 2f", cfg.scratch_reg, cfg.signature_data_reg)};
$sformatf("li x%0d, 0x1", cfg.gpr[0]),
$sformatf("bne x%0d, x%0d, 4f", cfg.scratch_reg, cfg.gpr[0])};
instr = {instr, str};
increment_csr(DPC, 4, instr);
str = {"2: nop"};
str = {"4: nop"};
instr = {instr, str};
// write DCSR to the testbench for any analysis
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DCSR));
@ -1242,7 +1267,7 @@ class riscv_asm_program_gen extends uvm_object;
debug_program.is_debug_program = 1;
debug_program.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(debug_program)
debug_program.gen_instr(.is_main_program(1'b1), .no_branch(1'b0));
debug_program.gen_instr(.is_main_program(1'b1), .no_branch(cfg.no_branch_jump));
gen_callstack(debug_program, debug_sub_program, debug_sub_program_name,
cfg.num_debug_sub_program);
debug_program.post_process_instr();
@ -1257,7 +1282,8 @@ class riscv_asm_program_gen extends uvm_object;
str = {$sformatf("csrwi 0x%0x, 0x0", DSCRATCH0)};
debug_end = {debug_end, str};
end
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, debug_end);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv,
cfg.sp, cfg.tp, debug_end);
// We have been using dscratch1 to store the
// value of our given scratch register for use in ebreak loop, so we
// need to restore its value before returning from D mode

View file

@ -257,16 +257,16 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
end
// addi sp,sp,-imm
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[0],
instr_name == ADDI; rd == SP; rs1 == SP;
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp;
imm == (~stack_len + 1);)
push_stack_instr[0].imm_str = $sformatf("-%0d", stack_len);
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SW; rs2 == saved_regs[i]; rs1 == SP; imm == 4 * (i+1);)
instr_name == SW; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
instr_name == SD; rs2 == saved_regs[i]; rs1 == SP; imm == 8 * (i+1);)
instr_name == SD; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
push_stack_instr[i+1].process_load_store = 0;
end
@ -340,16 +340,16 @@ class riscv_pop_stack_instr extends riscv_rand_instr_stream;
foreach(saved_regs[i]) begin
if(XLEN == 32) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LW; rd == saved_regs[i]; rs1 == SP; imm == 4 * (i+1);)
instr_name == LW; rd == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
end else begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
instr_name == LD; rd == saved_regs[i]; rs1 == SP; imm == 8 * (i+1);)
instr_name == LD; rd == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
end
pop_stack_instr[i].process_load_store = 0;
end
// addi sp,sp,imm
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[num_of_reg_to_save],
instr_name == ADDI; rd == SP; rs1 == SP; imm == stack_len;)
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;)
pop_stack_instr[num_of_reg_to_save].imm_str = $sformatf("%0d", stack_len);
mix_instr_stream(pop_stack_instr);
foreach(instr_list[i]) begin

View file

@ -95,6 +95,10 @@ class riscv_instr_base extends uvm_object;
imm_len <= 20;
}
constraint legal_operand_c {
(instr_name == C_LUI) -> (rd != SP);
}
constraint imm_val_c {
if(imm_type inside {NZIMM, NZUIMM}) {
imm != 0;

View file

@ -4,6 +4,10 @@ class riscv_instr_cov_item extends riscv_instr_base;
POSITIVE, NEGATIVE
} operand_sign_e;
typedef enum bit[1:0] {
DIV_NORMAL, DIV_BY_ZERO, DIV_OVERFLOW
} div_result_e;
typedef enum bit[1:0] {
EQUAL, LARGER, SMALLER
} compare_result_e;
@ -27,6 +31,7 @@ class riscv_instr_cov_item extends riscv_instr_base;
bit unaligned_mem_access;
bit compressed;
bit branch_hit;
div_result_e div_result;
operand_sign_e rs1_sign;
operand_sign_e rs2_sign;
operand_sign_e imm_sign;
@ -71,6 +76,9 @@ class riscv_instr_cov_item extends riscv_instr_base;
if (category == BRANCH) begin
branch_hit = is_branch_hit();
end
if (instr_name inside {DIV, DIVU, REM, REMU, DIVW, DIVUW, REMW, REMUW}) begin
div_result = get_div_result();
end
endfunction
function operand_sign_e get_operand_sign(bit [XLEN-1:0] value);
@ -101,6 +109,15 @@ class riscv_instr_cov_item extends riscv_instr_base;
end
endfunction
function div_result_e get_div_result();
if (rs2_value == 0) begin
return DIV_BY_ZERO;
end else if ((rs2_value == '1) && (rs1_value == (1'b1 << (XLEN-1))))
return DIV_OVERFLOW;
else
return DIV_NORMAL;
endfunction
function special_val_e get_operand_special_val(bit [XLEN-1:0] value);
if (value == 0) begin
return ZERO_VAL;
@ -114,7 +131,6 @@ class riscv_instr_cov_item extends riscv_instr_base;
endfunction
function special_val_e get_imm_special_val(bit [31:0] value);
void'(randomize(imm_len));
if (value == 0) begin
return ZERO_VAL;
end else if (format == U_FORMAT) begin

View file

@ -1,3 +1,20 @@
/*
* 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.
*/
`define INSTR_CG_BEGIN(INSTR_NAME) \
covergroup ``INSTR_NAME``_cg with function sample(riscv_instr_cov_item instr);
@ -70,11 +87,16 @@
cp_imm_sign : coverpoint instr.imm_sign; \
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`define J_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_imm_sign : coverpoint instr.imm_sign; \
cp_imm_lsb : coverpoint instr.imm[1:0]; \
cp_rd : coverpoint instr.rd; \
`define CSR_INSTR_CG_BEGIN(INSTR_NAME) \
`INSTR_CG_BEGIN(INSTR_NAME) \
cp_csr : coverpoint instr.csr { \
bins csr[] = cp_csr with (is_implemented_csr(item)); \
} \
cp_rs1 : coverpoint instr.rs1; \
cp_rd : coverpoint instr.rd; \
cp_gpr_harzard : coverpoint instr.gpr_hazard;
@ -160,8 +182,7 @@
`define CG_END endgroup
class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
riscv_instr_pkg::implemented_csr);
class riscv_instr_cover_group;
riscv_instr_gen_config cfg;
riscv_instr_cov_item cur_instr;
@ -170,7 +191,6 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
int unsigned instr_cnt;
int unsigned branch_instr_cnt;
bit [4:0] branch_hit_history; // The last 5 branch result
privileged_reg_t privil_csr[$];
///////////// RV32I instruction functional coverage //////////////
@ -208,16 +228,28 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`I_INSTR_CG_BEGIN(srai)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
`INSTR_CG_BEGIN(srai)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
`I_INSTR_CG_BEGIN(slli)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
`INSTR_CG_BEGIN(slli)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
`I_INSTR_CG_BEGIN(srli)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
`INSTR_CG_BEGIN(srli)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
// Logical instructions
@ -324,6 +356,15 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
cp_misalign: coverpoint instr.unaligned_mem_access;
`CG_END
// JUMP instruction
`J_INSTR_CG_BEGIN(jal)
`CG_END
`J_INSTR_CG_BEGIN(jalr)
cp_rs1_eq_rd : coverpoint instr.rs1 iff (instr.rs1 == instr.rd);
cp_rs1_ne_rd : coverpoint instr.rs1 iff (instr.rs1 != instr.rd);
`CG_END
// CSR instructions
`CSR_INSTR_CG_BEGIN(csrrw)
cp_rs2 : coverpoint instr.rs1;
@ -349,6 +390,12 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
cp_imm_sign : coverpoint instr.imm_sign;
`CG_END
covergroup rv32i_misc_cg with function sample(riscv_instr_cov_item instr);
cp_misc : coverpoint instr.instr_name {
bins instr[] = {FENCE, FENCE_I, EBREAK, ECALL, MRET, WFI};
}
endgroup
// RV32M
`R_INSTR_CG_BEGIN(mul)
@ -368,48 +415,66 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
`CG_END
`R_INSTR_CG_BEGIN(div)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(divu)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(rem)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(remu)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
// RV64M
// Below instructions only do calculation based on lower 32 bits, and extend the result to 64
// bits. Add special covergroup for corner cases
`R_INSTR_CG_BEGIN(mulw)
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(divw)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_div_zero : coverpoint instr.rs2_value iff (instr.rs2_value[31:0] == 0) {
bins zero = {0};
bins non_zero = default;
}
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(divuw)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_div_zero : coverpoint instr.rs2_value iff (instr.rs2_value[31:0] == 0) {
bins zero = {0};
bins non_zero = default;
}
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(remw)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_div_zero : coverpoint instr.rs2_value iff (instr.rs2_value[31:0] == 0) {
bins zero = {0};
bins non_zero = default;
}
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`R_INSTR_CG_BEGIN(remuw)
cp_rs2_val : coverpoint instr.rs2_special_val;
cp_div_result: coverpoint instr.div_result;
cp_div_zero : coverpoint instr.rs2_value iff (instr.rs2_value[31:0] == 0) {
bins zero = {0};
bins non_zero = default;
}
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
@ -438,16 +503,41 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
cp_sign_cross: cross cp_rs1_sign, cp_rs2_sign;
`CG_END
`I_INSTR_CG_BEGIN(sraiw)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
// imm[5] could be 1 for RV64I SLLI/SRAI/SRLI
`INSTR_CG_BEGIN(srai64)
cp_imm: coverpoint instr.imm[5];
`CG_END
`I_INSTR_CG_BEGIN(slliw)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
`INSTR_CG_BEGIN(slli64)
cp_imm: coverpoint instr.imm[5];
`CG_END
`I_INSTR_CG_BEGIN(srliw)
cp_sign_cross: cross cp_rs1_sign, cp_imm_sign;
`INSTR_CG_BEGIN(srli64)
cp_imm: coverpoint instr.imm[5];
`CG_END
`INSTR_CG_BEGIN(sraiw)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
`INSTR_CG_BEGIN(slliw)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
`INSTR_CG_BEGIN(srliw)
cp_rs1 : coverpoint instr.rs1;
cp_rd : coverpoint instr.rd;
cp_rs1_sign : coverpoint instr.rs1_sign;
cp_rd_sign : coverpoint instr.rd_sign;
cp_gpr_harzard : coverpoint instr.gpr_hazard;
`CG_END
`R_INSTR_CG_BEGIN(addw)
@ -518,13 +608,31 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
`CB_INSTR_CG_BEGIN(c_bnez)
`CG_END
`CB_INSTR_CG_BEGIN(c_srli)
`INSTR_CG_BEGIN(c_srli)
cp_rs1 : coverpoint instr.rs1 {
bins gpr[] = cp_rs1 with (is_compressed_gpr(riscv_reg_t'(item)));
}
cp_gpr_harzard : coverpoint instr.gpr_hazard {
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD};
}
`CG_END
`CB_INSTR_CG_BEGIN(c_srai)
`INSTR_CG_BEGIN(c_srai)
cp_rs1 : coverpoint instr.rs1 {
bins gpr[] = cp_rs1 with (is_compressed_gpr(riscv_reg_t'(item)));
}
cp_gpr_harzard : coverpoint instr.gpr_hazard {
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD};
}
`CG_END
`CI_INSTR_CG_BEGIN(c_slli)
`INSTR_CG_BEGIN(c_slli)
cp_rs1 : coverpoint instr.rs1 {
bins gpr[] = cp_rs1 with (is_compressed_gpr(riscv_reg_t'(item)));
}
cp_gpr_harzard : coverpoint instr.gpr_hazard {
bins valid_hazard[] = {NO_HAZARD, RAW_HAZARD};
}
`CG_END
`CJ_INSTR_CG_BEGIN(c_j)
@ -588,9 +696,35 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
}
`CG_END
// Cover all illegal instruction
covergroup illegal_cg with function sample(bit [31:0] binary);
cp_point : coverpoint binary {
wildcard bins c_addi4spn = {32'bxxxx_xxxxx_0000_0000_000x_xx00};
wildcard bins c_addiw = {32'bxxxx_xxxxx_001x_0000_0xxx_xx01};
wildcard bins c_addi16sp = {32'bxxxx_xxxxx_0110_0001_0000_0001};
wildcard bins c_lui = {32'bxxxx_xxxxx_0110_xxxx_1000_0001,
32'bxxxx_xxxxx_0110_xx1x_x000_0001,
32'bxxxx_xxxxx_0110_x1xx_x000_0001,
32'bxxxx_xxxxx_0110_1xxx_x000_0001};
wildcard bins c_jr = {32'bxxxx_xxxxx_1000_0000_0000_0001};
}
endgroup
// Cover all non-compressed opcode
covergroup opcode_cg with function sample(bit [4:0] opcode);
cp_opcode: coverpoint opcode;
endgroup
// Cover all compressed instruction opcode
covergroup compressed_opcode_cg with function sample(bit [15:0] binary);
cp_00 : coverpoint binary[15:13] iff (binary[1:0] == 2'b00);
cp_01 : coverpoint binary[15:13] iff (binary[1:0] == 2'b01);
cp_10 : coverpoint binary[15:13] iff (binary[1:0] == 2'b10);
endgroup
// Branch hit history
covergroup branch_hit_history_cg;
coverpoint branch_hit_history;
cp_branch_history: coverpoint branch_hit_history;
endgroup
// Instruction transition for all supported instructions
@ -613,7 +747,36 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
endgroup
*/
// TODO: Add covergroup for various hazard conditions
covergroup privileged_csr_cg with function sample(bit [11:0] csr);
cp_csr : coverpoint csr {
bins pcsr[] = cp_csr with (item inside {implemented_csr});
}
endgroup
// Privileged CSR covergroup
covergroup mcause_exception_cg with function sample(exception_cause_t exception);
cp_exception: coverpoint exception {
bins exception[] = cp_exception with (item inside {implemented_exception});
}
endgroup
covergroup mcause_interrupt_cg with function sample(interrupt_cause_t interrupt);
cp_interrupt: coverpoint interrupt {
bins interrupt[] = cp_interrupt with (item inside {implemented_interrupt});
}
endgroup
covergroup mepc_cg with function sample(bit [XLEN-1:0] val);
cp_align: coverpoint val[1:0] {
bins alignment[] = {2'b00, 2'b10};
}
endgroup
covergroup mstatus_m_cg with function sample(bit [XLEN-1:0] val);
cp_mie : coverpoint val[3];
cp_mpie : coverpoint val[7];
cp_mpp : coverpoint val[12:11];
endgroup
function new(riscv_instr_gen_config cfg);
this.cfg = cfg;
@ -643,6 +806,8 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
sltu_cg = new();
slti_cg = new();
sltiu_cg = new();
jal_cg = new();
jalr_cg = new();
beq_cg = new();
bne_cg = new();
blt_cg = new();
@ -665,6 +830,10 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
csrrci_cg = new();
// instr_trans_cg = new();
branch_hit_history_cg = new();
rv32i_misc_cg = new();
illegal_cg = new();
opcode_cg = new();
compressed_opcode_cg = new();
if (RV32M inside {supported_isa}) begin
mul_cg = new();
mulh_cg = new();
@ -685,6 +854,9 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
lwu_cg = new();
ld_cg = new();
sd_cg = new();
slli64_cg = new();
srli64_cg = new();
srai64_cg = new();
sllw_cg = new();
slliw_cg = new();
srlw_cg = new();
@ -731,6 +903,11 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
c_subw_cg = new();
c_addw_cg = new();
end
privileged_csr_cg = new();
mcause_exception_cg = new();
mcause_interrupt_cg = new();
mepc_cg = new();
mstatus_m_cg = new();
endfunction
function void sample(riscv_instr_cov_item instr);
@ -740,6 +917,9 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
end
if (instr.binary[1:0] != 2'b11) begin
hint_cg.sample(instr);
compressed_opcode_cg.sample(instr.binary[15:0]);
end else begin
opcode_cg.sample(instr.binary[6:2]);
end
case (instr.instr_name)
ADD : add_cg.sample(instr);
@ -750,7 +930,24 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
SLL : sll_cg.sample(instr);
SRL : srl_cg.sample(instr);
SRA : sra_cg.sample(instr);
SLLI : slli_cg.sample(instr);
SLLI : begin
slli_cg.sample(instr);
if (RV64I inside {supported_isa}) begin
slli64_cg.sample(instr);
end
end
SRLI : begin
srli_cg.sample(instr);
if (RV64I inside {supported_isa}) begin
srli64_cg.sample(instr);
end
end
SRAI : begin
slli_cg.sample(instr);
if (RV64I inside {supported_isa}) begin
slli64_cg.sample(instr);
end
end
SRLI : srli_cg.sample(instr);
SRAI : srai_cg.sample(instr);
AND : and_cg.sample(instr);
@ -763,6 +960,8 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
SLTU : sltu_cg.sample(instr);
SLTI : slti_cg.sample(instr);
SLTIU : sltiu_cg.sample(instr);
JAL : jal_cg.sample(instr);
JALR : jalr_cg.sample(instr);
BEQ : beq_cg.sample(instr);
BNE : bne_cg.sample(instr);
BLT : blt_cg.sample(instr);
@ -839,6 +1038,12 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
C_SUBW : c_subw_cg.sample(instr);
C_ADDW : c_addw_cg.sample(instr);
C_ADDIW : c_addiw_cg.sample(instr);
default: begin
illegal_cg.sample(instr.binary);
if (instr.group == RV32I) begin
rv32i_misc_cg.sample(instr);
end
end
endcase
if (instr.category == BRANCH) begin
branch_hit_history = (branch_hit_history << 1) | instr.branch_hit;
@ -847,6 +1052,28 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
branch_hit_history_cg.sample();
end
end
if (instr.category == CSR) begin
privileged_csr_cg.sample(instr.csr);
case (instr.csr)
MCAUSE: begin
if (instr.rd_value[XLEN-1]) begin
interrupt_cause_t interrupt;
if ($cast(interrupt, instr.rd_value[3:0])) begin
mcause_interrupt_cg.sample(interrupt);
end
end else begin
exception_cause_t exception;
if ($cast(exception, instr.rd_value[3:0])) begin
mcause_exception_cg.sample(exception);
end
end
end
MEPC: mepc_cg.sample(instr.rd_value);
MSTATUS: begin
mstatus_m_cg.sample(instr.rd_value);
end
endcase
end
if (instr_cnt > 1) begin
// instr_trans_cg.sample();
end
@ -854,15 +1081,6 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
pre_instr.mem_addr = instr.mem_addr;
endfunction
// Check if the privileged CSR is implemented
virtual function bit is_implemented_csr(bit [11:0] pcsr);
if (pcsr inside {implemented_pcsr}) begin
return 1'b1;
end else begin
return 1'b0;
end
endfunction
// Check if the instruction is supported
virtual function bit is_supported_instr(riscv_instr_name_t name);
if (name inside {instr_list}) begin
@ -885,9 +1103,6 @@ class riscv_instr_cover_group#(privileged_reg_t implemented_pcsr[] =
virtual function void build_instr_list();
riscv_instr_name_t instr_name;
instr_name = instr_name.first;
foreach (riscv_instr_pkg::implemented_csr[i]) begin
privil_csr.push_back(riscv_instr_pkg::implemented_csr[i]);
end
do begin
riscv_instr_base instr;
if (!(instr_name inside {unsupported_instr}) && (instr_name != INVALID_INSTR)) begin

View file

@ -64,6 +64,15 @@ class riscv_instr_gen_config extends uvm_object;
// Enable sfence.vma instruction
rand bit enable_sfence;
// Reserved register
// Reserved for various hardcoded routines
rand riscv_reg_t gpr[4];
// Used by any DCSR operations inside of the debug rom
rand riscv_reg_t scratch_reg;
// Use a random register for stack pointer/thread pointer
rand riscv_reg_t sp;
rand riscv_reg_t tp;
// Options for privileged mode CSR checking
// Below checking can be made optional as the ISS implementation could be different with the
// processor.
@ -155,15 +164,11 @@ class riscv_instr_gen_config extends uvm_object;
bit force_m_delegation = 0;
bit force_s_delegation = 0;
bit support_supervisor_mode;
bit disable_compressed_instr;
// "Memory mapped" address that when written to will indicate some event to
// the testbench - testbench will take action based on the value written
int signature_addr = 32'hdead_beef;
bit require_signature_addr = 1'b0;
rand riscv_reg_t signature_addr_reg;
rand riscv_reg_t signature_data_reg;
// Register that will be used to handle any DCSR operations inside of the
// debug rom
rand riscv_reg_t scratch_reg;
// Enable a full or empty debug_rom section.
// Full debug_rom will contain random instruction streams.
// Empty debug_rom will contain just dret instruction and will return immediately.
@ -177,6 +182,10 @@ class riscv_instr_gen_config extends uvm_object;
bit set_dcsr_ebreak = 1'b0;
// Number of sub programs in the debug rom
int num_debug_sub_program = 0;
// Enable debug single stepping
bit enable_debug_single_step = 0;
// Number of single stepping iterations
rand int single_step_iterations;
// Stack space allocated to each program, need to be enough to store necessary context
// Example: RA, SP, T0
int min_stack_len_per_program = 10 * (XLEN/8);
@ -186,9 +195,6 @@ class riscv_instr_gen_config extends uvm_object;
// Maximum directed instruction stream sequence count
int max_directed_instr_stream_seq = 20;
// Reserved registers
// Default reserved registers, only used by special instructions
riscv_reg_t default_reserved_regs[];
// All reserved regs
riscv_reg_t reserved_regs[];
uvm_cmdline_processor inst;
@ -235,6 +241,13 @@ class riscv_instr_gen_config extends uvm_object;
`endif
}
// Keep the number of single step iterations relatively small
constraint debug_single_step_c {
if (enable_debug_single_step) {
single_step_iterations inside {[10 : 50]};
}
}
// Boot privileged mode distribution
constraint boot_privileged_mode_dist_c {
// Boot to higher privileged mode more often
@ -305,25 +318,23 @@ class riscv_instr_gen_config extends uvm_object;
}
}
constraint reserve_scratch_reg_c {
scratch_reg != ZERO;
foreach (default_reserved_regs[i]) {
signature_data_reg != default_reserved_regs[i];
signature_addr_reg != default_reserved_regs[i];
}
constraint sp_tp_c {
sp != tp;
!(sp inside {GP, RA, ZERO});
!(tp inside {GP, RA, ZERO});
}
constraint signature_addr_c {
if (require_signature_addr) {
foreach (default_reserved_regs[i]) {
signature_addr_reg != default_reserved_regs[i];
signature_data_reg != default_reserved_regs[i];
}
signature_data_reg != scratch_reg;
signature_addr_reg != scratch_reg;
signature_data_reg != ZERO;
signature_addr_reg != ZERO;
constraint reserve_scratch_reg_c {
scratch_reg != ZERO;
scratch_reg != sp;
scratch_reg != tp;
}
constraint gpr_c {
foreach (gpr[i]) {
!(gpr[i] inside {sp, tp, scratch_reg, ZERO, RA, GP});
}
unique {gpr};
}
constraint addr_translaction_c {
@ -343,7 +354,6 @@ class riscv_instr_gen_config extends uvm_object;
function new (string name = "");
string s;
super.new(name);
setup_default_reserved_regs();
init_delegation();
inst = uvm_cmdline_processor::get_inst();
get_int_arg_value("+num_of_tests=", num_of_tests);
@ -369,6 +379,7 @@ class riscv_instr_gen_config extends uvm_object;
get_bool_arg_value("+force_m_delegation=", force_m_delegation);
get_bool_arg_value("+force_s_delegation=", force_s_delegation);
get_bool_arg_value("+require_signature_addr=", require_signature_addr);
get_bool_arg_value("+disable_compressed_instr=", disable_compressed_instr);
get_bool_arg_value("+randomize_csr=", randomize_csr);
if (this.require_signature_addr) begin
get_hex_arg_value("+signature_addr=", signature_addr);
@ -378,6 +389,7 @@ class riscv_instr_gen_config extends uvm_object;
get_int_arg_value("+num_debug_sub_program=", num_debug_sub_program);
get_bool_arg_value("+enable_ebreak_in_debug_rom=", enable_ebreak_in_debug_rom);
get_bool_arg_value("+set_dcsr_ebreak=", set_dcsr_ebreak);
get_bool_arg_value("+enable_debug_single_step=", enable_debug_single_step);
if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin
`uvm_info(get_full_name(), $sformatf(
"Got boot mode option - %0s", boot_mode_opts), UVM_LOW)
@ -434,14 +446,6 @@ class riscv_instr_gen_config extends uvm_object;
while(intr_cause != intr_cause.first);
endfunction
// Reserve below registers for special purpose instruction
// The other normal instruction cannot use them as destination register
virtual function void setup_default_reserved_regs();
default_reserved_regs = {SP, // x2, stack pointer (user stack)
TP // x4, thread pointer, used as kernel stack pointer
};
endfunction
function void pre_randomize();
foreach (riscv_instr_pkg::supported_privileged_mode[i]) begin
if(riscv_instr_pkg::supported_privileged_mode[i] == SUPERVISOR_MODE)
@ -449,9 +453,12 @@ class riscv_instr_gen_config extends uvm_object;
end
endfunction
function void get_non_reserved_gpr();
endfunction
function void post_randomize();
// Setup the list all reserved registers
reserved_regs = {default_reserved_regs, scratch_reg};
reserved_regs = {tp, sp, scratch_reg};
// Need to save all loop registers, and RA/T0
min_stack_len_per_program = 2 * (XLEN/8);
// Check if the setting is legal
@ -520,7 +527,8 @@ class riscv_instr_gen_config extends uvm_object;
if (!(instr_name inside {unsupported_instr, excluded_instr})) begin
instr = riscv_instr_base::type_id::create("instr");
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr, instr_name == local::instr_name;)
if (instr.group inside {supported_isa}) begin
if ((instr.group inside {supported_isa}) &&
!(disable_compressed_instr && instr.is_compressed)) begin
`uvm_info(`gfn, $sformatf("Adding [%s] %s to the list",
instr.group.name(), instr.instr_name.name()), UVM_HIGH)
instr_group[instr.group].push_back(instr_name);

View file

@ -759,14 +759,16 @@ package riscv_instr_pkg;
function automatic void push_gpr_to_kernel_stack(privileged_reg_t status,
privileged_reg_t scratch,
bit mprv,
riscv_reg_t sp,
riscv_reg_t tp,
ref string instr[$]);
string store_instr = (XLEN == 32) ? "sw" : "sd";
if (scratch inside {implemented_csr}) begin
// Use kernal stack for handling exceptions
// Save the user mode stack pointer to the scratch register
instr.push_back($sformatf("csrrw sp, 0x%0x, sp", scratch));
instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", sp, scratch, sp));
// Move TP to SP
instr.push_back("add sp, tp, zero");
instr.push_back($sformatf("add x%0d, x%0d, zero", sp, tp));
end
// 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
@ -775,21 +777,21 @@ package riscv_instr_pkg;
// 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) begin
instr.push_back($sformatf("csrr tp, 0x%0x // MSTATUS", status));
instr.push_back("srli tp, tp, 11"); // Move MPP to bit 0
instr.push_back("andi tp, tp, 0x3"); // keep the MPP bits
instr.push_back("xori tp, tp, 0x3"); // Check if MPP equals to M-mode('b11)
instr.push_back("bnez tp, 1f"); // Use physical address for kernel SP
instr.push_back($sformatf("csrr x%0d, 0x%0x // MSTATUS", tp, status));
instr.push_back($sformatf("srli x%0d, x%0d, 11", tp, tp)); // Move MPP to bit 0
instr.push_back($sformatf("andi x%0d, x%0d, 0x3", tp, tp)); // keep the MPP bits
instr.push_back($sformatf("xori x%0d, x%0d, 0x3", tp, tp)); // Check if MPP equals to M-mode('b11)
instr.push_back($sformatf("bnez x%0d, 1f", tp)); // Use physical address for kernel SP
// Use virtual address for stack pointer
instr.push_back($sformatf("slli sp, sp, %0d", XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("srli sp, sp, %0d", XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("slli x%0d, x%0d, %0d", sp, sp, XLEN - MAX_USED_VADDR_BITS));
instr.push_back($sformatf("srli x%0d, x%0d, %0d", sp, sp, XLEN - MAX_USED_VADDR_BITS));
end
end
// Reserve space from kernel stack to save all 32 GPR except for x0
instr.push_back($sformatf("1: addi sp, sp, -%0d", 31 * (XLEN/8)));
instr.push_back($sformatf("1: addi x%0d, x%0d, -%0d", sp, sp, 31 * (XLEN/8)));
// Push all GPRs to kernel stack
for(int i = 1; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(sp)", store_instr, i, i * (XLEN/8)));
instr.push_back($sformatf("%0s x%0d, %0d(x%0d)", store_instr, i, i * (XLEN/8), sp));
end
endfunction
@ -797,19 +799,21 @@ package riscv_instr_pkg;
function automatic void pop_gpr_from_kernel_stack(privileged_reg_t status,
privileged_reg_t scratch,
bit mprv,
riscv_reg_t sp,
riscv_reg_t tp,
ref string instr[$]);
string load_instr = (XLEN == 32) ? "lw" : "ld";
// Pop user mode GPRs from kernel stack
for(int i = 1; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(sp)", load_instr, i, i * (XLEN/8)));
instr.push_back($sformatf("%0s x%0d, %0d(x%0d)", load_instr, i, i * (XLEN/8), sp));
end
// Restore kernel stack pointer
instr.push_back($sformatf("addi sp, sp, %0d", 31 * (XLEN/8)));
instr.push_back($sformatf("addi x%0d, x%0d, %0d", sp, sp, 31 * (XLEN/8)));
if (scratch inside {implemented_csr}) begin
// Move SP to TP
instr.push_back("add tp, sp, zero");
instr.push_back($sformatf("add x%0d, x%0d, zero", tp, sp));
// Restore user mode stack pointer
instr.push_back($sformatf("csrrw sp, 0x%0x, sp", scratch));
instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", sp, scratch, sp));
end
endfunction

View file

@ -209,7 +209,7 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
!(instr_name inside {EBREAK, C_EBREAK});)
end else begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};)
instr_name inside {allowed_instr};)
end
instr.copy_base_instr(cfg.instr_template[instr_name]);
`uvm_info(`gfn, $sformatf("%s: rs1:%0d, rs2:%0d, rd:%0d, imm:%0d",
@ -235,9 +235,15 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
instr.rs2 = instr.gen_rand_gpr(.included_reg(avail_regs));
end
if (instr.has_rd && !skip_rd) begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
if (instr_name == C_LUI) begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs, SP}));
end else begin
instr.rd = instr.gen_rand_gpr(
.included_reg(avail_regs),
.excluded_reg({reserved_rd, cfg.reserved_regs}));
end
end
if ((instr.category == CSR) && !skip_csr) begin
instr.gen_rand_csr(.privileged_mode(cfg.init_privileged_mode),

View file

@ -108,7 +108,7 @@ class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
},
"Cannot randomize avail_regs")
end
if (rs1_reg inside {[S0 : A5]}) begin
if ((rs1_reg inside {[S0 : A5]}) && !cfg.disable_compressed_instr) begin
enable_compressed_load_store = 1;
end
foreach(addr[i]) begin

View file

@ -39,14 +39,14 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
solve num_of_nested_loop before loop_limit_reg;
foreach (loop_cnt_reg[i]) {
loop_cnt_reg[i] != ZERO;
foreach (cfg.default_reserved_regs[j]) {
loop_cnt_reg[i] != cfg.default_reserved_regs[j];
foreach (cfg.reserved_regs[j]) {
loop_cnt_reg[i] != cfg.reserved_regs[j];
}
}
foreach (loop_limit_reg[i]) {
loop_limit_reg[i] != ZERO;
foreach (cfg.default_reserved_regs[j]) {
loop_limit_reg[i] != cfg.default_reserved_regs[j];
foreach (cfg.reserved_regs[j]) {
loop_limit_reg[i] != cfg.reserved_regs[j];
}
}
unique {loop_cnt_reg, loop_limit_reg};

View file

@ -244,6 +244,7 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// 2. For normal test, a page table fault typically means the program is accessing a large
// virtual address which currently not mapped a valid physical address. Need to do a
// memcpy to move data from lower physical address to the place the virtual address map to.
// TODO: Refactor this part with new reserved GPR
virtual function void gen_page_fault_handling_routine(ref string instr[$]);
int unsigned level;
string load_store_unit;
@ -394,7 +395,7 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
instr.push_back("j_smode: jal ra, smode_program");
instr.push_back("fix_pte_ret:");
// Recover the user mode GPR from kernal stack
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
foreach(instr[i]) begin
@ -437,7 +438,8 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// Assign the PPN of link PTE to link the page tables together
foreach(page_table[i]) begin
if (page_table[i].level == 0) break;
instr = {instr, $sformatf("la x21, page_table_%0d+2048 # Process PT_%0d", i, i)};
instr = {instr, $sformatf("la x%0d, page_table_%0d+2048 # Process PT_%0d",
cfg.gpr[1], i, i)};
foreach(page_table[i].pte[j]) begin
if(j >= SUPER_LEAF_PTE_PER_TABLE) continue;
pte_addr_offset = (j * PTE_SIZE) - 2048;
@ -445,19 +447,21 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
i, j, page_table[i].pte[j].v, page_table[i].level), UVM_LOW)
if(page_table[i].pte[j].xwr == NEXT_LEVEL_PAGE) begin
// Use the target table address as PPN of this PTE
// x20 holds the target table physical address
// x%0d holds the target table physical address
instr = {instr,
// Load the current PTE value
$sformatf("l%0s x22, %0d(x21)", load_store_unit, pte_addr_offset),
$sformatf("l%0s x%0d, %0d(x%0d)",
load_store_unit, cfg.gpr[2], pte_addr_offset, cfg.gpr[1]),
// Load the target page table physical address, PPN should be 0
$sformatf("la x20, page_table_%0d # Link PT_%0d_PTE_%0d -> PT_%0d",
$sformatf("la x%0d, page_table_%0d # Link PT_%0d_PTE_%0d -> PT_%0d", cfg.gpr[0],
get_child_table_id(i, j), i, j, get_child_table_id(i, j)),
// Right shift the address for 2 bits to the correct PPN position in PTE
$sformatf("srli x20, x20, 2"),
$sformatf("srli x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
// Assign PPN
"or x22, x20, x22",
$sformatf("or x%0d, x%0d, x%0d", cfg.gpr[2], cfg.gpr[0], cfg.gpr[2]),
// Store the new PTE value
$sformatf("s%0s x22, %0d(x21)", load_store_unit, pte_addr_offset)};
$sformatf("s%0s x%0d, %0d(x%0d)",
load_store_unit, cfg.gpr[2], pte_addr_offset, cfg.gpr[1])};
end
end
end
@ -467,54 +471,60 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
if (cfg.support_supervisor_mode) begin
instr = {instr,
// Process kernel instruction pages
"la x20, _kernel_instr_start",
"la x21, _kernel_instr_end",
$sformatf("la x%0d, _kernel_instr_start", cfg.gpr[0]),
$sformatf("la x%0d, _kernel_instr_end", cfg.gpr[1]),
// Get the VPN of the physical address
$sformatf("slli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x20, x20, %0d", $clog2(XLEN)),
$sformatf("slli x21, x21, %0d", XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x21, x21, %0d", XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x21, x21, %0d", $clog2(XLEN)),
$sformatf("slli x%0d, x%0d, %0d",
cfg.gpr[0], cfg.gpr[0], XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x%0d, x%0d, %0d",
cfg.gpr[0], cfg.gpr[0], XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], $clog2(XLEN)),
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[1], cfg.gpr[1],
XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[1], cfg.gpr[1],
XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[1], cfg.gpr[1], $clog2(XLEN)),
// Starting from the first 4KB leaf page table
$sformatf("la x22, page_table_%0d", get_1st_4k_table_id()),
"add x20, x22, x20",
"add x21, x22, x21",
$sformatf("li x22, 0x%0x", ubit_mask),
$sformatf("la x%0d, page_table_%0d", cfg.gpr[2], get_1st_4k_table_id()),
$sformatf("add x%0d, x%0d, x%0d", cfg.gpr[0], cfg.gpr[2], cfg.gpr[0]),
$sformatf("add x%0d, x%0d, x%0d", cfg.gpr[1], cfg.gpr[2], cfg.gpr[1]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[2], ubit_mask),
"1:",
// Load the PTE from the memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
$sformatf("l%0s x%0d, 0(x%0d)", load_store_unit, cfg.gpr[3], cfg.gpr[0]),
// Unset U bit
"and x23, x23, x22",
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
// Save PTE back to memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
$sformatf("l%0s x%0d, 0(x%0d)", load_store_unit, cfg.gpr[3], cfg.gpr[0]),
// Move to the next PTE
$sformatf("addi x20, x20, %0d", XLEN/8),
$sformatf("addi x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN/8),
// If not the end of the kernel space, process the next PTE
"ble x20, x21, 1b",
$sformatf("ble x%0d, x%0d, 1b", cfg.gpr[0], cfg.gpr[1]),
// Process kernel data pages
"la x20, _kernel_data_start",
$sformatf("la x%0d, _kernel_data_start", cfg.gpr[0]),
// Get the VPN of the physical address
$sformatf("slli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x20, x20, %0d", XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x20, x20, %0d", $clog2(XLEN)),
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0],
XLEN - MAX_USED_VADDR_BITS),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0],
XLEN - MAX_USED_VADDR_BITS + 12),
$sformatf("slli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], $clog2(XLEN)),
// Starting from the first 4KB leaf page table
$sformatf("la x22, page_table_%0d", get_1st_4k_table_id()),
"add x20, x22, x20",
$sformatf("li x22, 0x%0x", ubit_mask),
$sformatf("la x%0d, page_table_%0d", cfg.gpr[2], get_1st_4k_table_id()),
$sformatf("add x%0d, x%0d, x%0d", cfg.gpr[0], cfg.gpr[2], cfg.gpr[0]),
$sformatf("li x%0d, 0x%0x", cfg.gpr[2], ubit_mask),
// Assume 20 PTEs for kernel data pages
$sformatf("addi x20, x20, %0d", 20 * XLEN/8),
$sformatf("addi x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], 20 * XLEN/8),
"2:",
// Load the PTE from the memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
$sformatf("l%0s x%0d, 0(x%0d)", load_store_unit, cfg.gpr[3], cfg.gpr[0]),
// Unset U bit
"and x23, x23, x22",
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
// Save PTE back to memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
$sformatf("l%0s x%0d, 0(x%0d)", load_store_unit, cfg.gpr[3], cfg.gpr[0]),
// Move to the next PTE
$sformatf("addi x20, x20, %0d", XLEN/8),
$sformatf("addi x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN/8),
// If not the end of the kernel space, process the next PTE
"ble x20, x21, 2b"};
$sformatf("ble x%0d, x%0d, 2b", cfg.gpr[0], cfg.gpr[1])};
end
endfunction

View file

@ -169,9 +169,9 @@ class riscv_privileged_common_seq extends uvm_sequence;
virtual function void gen_csr_instr(riscv_privil_reg regs[$], ref string instrs[$]);
foreach(regs[i]) begin
instrs.push_back($sformatf("li a0, 0x%0x", regs[i].get_val()));
instrs.push_back($sformatf("csrw 0x%0x, a0 # %0s",
regs[i].reg_name, regs[i].reg_name.name()));
instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[0], regs[i].get_val()));
instrs.push_back($sformatf("csrw 0x%0x, x%0d # %0s",
regs[i].reg_name, cfg.gpr[0], regs[i].reg_name.name()));
end
endfunction
@ -182,17 +182,17 @@ class riscv_privileged_common_seq extends uvm_sequence;
satp = riscv_privil_reg::type_id::create("satp");
satp.init_reg(SATP);
satp.set_field("MODE", SATP_MODE);
instrs.push_back($sformatf("li a0, 0x%0x", satp.get_val()));
instrs.push_back($sformatf("csrw 0x%0x, a0 // satp", SATP));
instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[0], satp.get_val()));
instrs.push_back($sformatf("csrw 0x%0x, x%0d // satp", SATP, cfg.gpr[0]));
satp_ppn_mask = '1 >> (XLEN - satp.get_field_by_name("PPN").bit_width);
// Load the root page table physical address
instrs.push_back("la a0, page_table_0");
instrs.push_back($sformatf("la x%0d, page_table_0", cfg.gpr[0]));
// Right shift to get PPN at 4k granularity
instrs.push_back("srli a0, a0, 12");
instrs.push_back($sformatf("li a1, 0x%0x", satp_ppn_mask));
instrs.push_back("and a0, a0, a1");
instrs.push_back($sformatf("srli x%0d, x%0d, 12", cfg.gpr[0], cfg.gpr[0]));
instrs.push_back($sformatf("li x%0d, 0x%0x", cfg.gpr[1], satp_ppn_mask));
instrs.push_back($sformatf("and x%0d, x%0d, x%0d", cfg.gpr[0], cfg.gpr[0], cfg.gpr[1]));
// Set the PPN field for SATP
instrs.push_back($sformatf("csrs 0x%0x, a0 // satp", SATP));
instrs.push_back($sformatf("csrs 0x%0x, x%0d // satp", SATP, cfg.gpr[0]));
endfunction
endclass

View file

@ -88,6 +88,9 @@ class riscv_rand_instr extends riscv_instr_base;
if(cfg.no_branch_jump) {
category != BRANCH;
}
if (cfg.disable_compressed_instr) {
!(group inside {RV32C, RV64C, RV128C, RV32FC, RV32DC});
}
}
constraint csr_instr_c {

View file

@ -12,17 +12,23 @@ class riscv_instr_cov_debug_test extends uvm_test;
task run_phase(uvm_phase phase);
bit [XLEN-1:0] rand_val;
int finished_cnt;
void'($value$plusargs("num_of_iterations=%0d", num_of_iterations));
cfg = riscv_instr_gen_config::type_id::create("cfg");
instr = riscv_instr_cov_item::type_id::create("instr");
instr_cg = new(cfg);
repeat(20000) begin
void'(instr.randomize() with {group == RV32I;
csr inside {implemented_csr};});
`uvm_info(`gfn, instr.convert2asm(), UVM_LOW)
`uvm_info(`gfn, $sformatf("Randomizing %0d instructions", num_of_iterations), UVM_LOW);
for (int i=0; i<num_of_iterations; i++) begin
if (i - finished_cnt > 5000) begin
`uvm_info(`gfn, $sformatf("Progress: %0d/%0d", i, num_of_iterations), UVM_LOW)
finished_cnt = i;
end
void'(instr.randomize() with {csr inside {implemented_csr};});
`uvm_info(`gfn, instr.convert2asm(), UVM_HIGH)
instr.pre_sample();
instr_cg.sample(instr);
end
`uvm_info(`gfn, $sformatf("Randomizing %0d instructions", num_of_iterations), UVM_LOW);
`uvm_info("", "TEST PASSED", UVM_NONE);
endtask

View file

@ -113,6 +113,11 @@ class riscv_instr_cov_test extends uvm_test;
instr_cg.sample(instr);
return 1'b1;
end
end else if (trace["instr"] == "") begin
bit [XLEN-1:0] val;
get_val(trace["binary"], val);
instr_cg.illegal_cg.sample(val);
return 1'b1;
end
illegal_instr_cnt++;
return 1'b0;

View file

@ -64,7 +64,9 @@
-o design_opt"
sim:
cmd: >
vsim -64 -c -do <cwd>/questa_sim.tcl design_opt <sim_opts> -sv_seed <seed>
vsim -64 -c <cov_opts> -do <cwd>/questa_sim.tcl design_opt <sim_opts> -sv_seed <seed>
cov_opts: >
-do "coverage save -onexit cov.ucdb;"
- tool: dsim
env_var: DSIM,DSIM_LIB_PATH

View file

@ -36,8 +36,8 @@
+instr_cnt=10000
+num_of_sub_program=0
+no_fence=1
+no_data_page=1'b1
+no_branch_jump=1'b1
+no_data_page=1
+no_branch_jump=1
+boot_mode=m
iterations: 2
gen_test: riscv_instr_base_test
@ -91,14 +91,13 @@
+directed_instr_1=riscv_loop_instr,20
rtl_test: core_base_test
# TODO: Temporarily disable this as compiler seems to generate compressed instruction with rv64im
- test: riscv_non_compressed_instr_test
description: >
Random instruction test without compressed instructions
iterations: 0
iterations: 1
gen_test: riscv_rand_instr_test
gen_opts: >
+march=RV32I,RV32M,RV64I,RV64M
+disable_compressed_instr=1
gcc_opts: >
-march=rv64im
rtl_test: core_base_test