diff --git a/vendor/google_riscv-dv.lock.hjson b/vendor/google_riscv-dv.lock.hjson index 4852610b..c600e6f7 100644 --- a/vendor/google_riscv-dv.lock.hjson +++ b/vendor/google_riscv-dv.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/google/riscv-dv - rev: d3419444ca2fdb499a204587b2d36c6f5c1e0c44 + rev: ad6fe565a91445cc3ea3e32119360b57af4f19b2 } } diff --git a/vendor/google_riscv-dv/README.md b/vendor/google_riscv-dv/README.md index 3e727628..f6a617f9 100644 --- a/vendor/google_riscv-dv/README.md +++ b/vendor/google_riscv-dv/README.md @@ -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: /bin/riscv32-unknown-elf-gcc) +- Set environment variable RISCV_OBJCOPY to RISC-v objcopy executable + executable. (example: /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 diff --git a/vendor/google_riscv-dv/cov.py b/vendor/google_riscv-dv/cov.py index c7eccc70..e2052041 100644 --- a/vendor/google_riscv-dv/cov.py +++ b/vendor/google_riscv-dv/cov.py @@ -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 \"\"" % - (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=\"" % + (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("", 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() diff --git a/vendor/google_riscv-dv/run.py b/vendor/google_riscv-dv/run.py index b01eadcd..b2bbc710 100644 --- a/vendor/google_riscv-dv/run.py +++ b/vendor/google_riscv-dv/run.py @@ -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("", os.path.abspath(output_dir), sim_cmd) sim_cmd = re.sub("", cwd, sim_cmd) sim_cmd = re.sub("", 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("", 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__": diff --git a/vendor/google_riscv-dv/scripts/instr_trace_compare.py b/vendor/google_riscv-dv/scripts/instr_trace_compare.py index 86142336..dc9dab2d 100644 --- a/vendor/google_riscv-dv/scripts/instr_trace_compare.py +++ b/vendor/google_riscv-dv/scripts/instr_trace_compare.py @@ -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") diff --git a/vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py b/vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py index a64c2044..34ff24dd 100644 --- a/vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py +++ b/vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py @@ -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: diff --git a/vendor/google_riscv-dv/setting/riscv_core_setting.sv b/vendor/google_riscv-dv/setting/riscv_core_setting.sv index c481c5f2..284f6879 100644 --- a/vendor/google_riscv-dv/setting/riscv_core_setting.sv +++ b/vendor/google_riscv-dv/setting/riscv_core_setting.sv @@ -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 +}; diff --git a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv index b72821c5..a9bb07a0 100644 --- a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv +++ b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv index 38358328..c365a362 100644 --- a/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_instr_base.sv b/vendor/google_riscv-dv/src/riscv_instr_base.sv index 58f1046d..183addb5 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_base.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_base.sv @@ -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; diff --git a/vendor/google_riscv-dv/src/riscv_instr_cov_item.sv b/vendor/google_riscv-dv/src/riscv_instr_cov_item.sv index 064e00c5..c20ec665 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_cov_item.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_cov_item.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv b/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv index 5ac78ec2..19010264 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_cover_group.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv index 74de5748..3831ba7c 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv @@ -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); diff --git a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv index 187ffd28..7ccf0139 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_instr_stream.sv b/vendor/google_riscv-dv/src/riscv_instr_stream.sv index a412ee00..fc8c3fdb 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_stream.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_stream.sv @@ -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), diff --git a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv index 4c570fc2..dea27bfc 100644 --- a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_loop_instr.sv b/vendor/google_riscv-dv/src/riscv_loop_instr.sv index ea173de5..5e048ba6 100644 --- a/vendor/google_riscv-dv/src/riscv_loop_instr.sv +++ b/vendor/google_riscv-dv/src/riscv_loop_instr.sv @@ -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}; diff --git a/vendor/google_riscv-dv/src/riscv_page_table_list.sv b/vendor/google_riscv-dv/src/riscv_page_table_list.sv index 1ce35ed6..5a80d68e 100644 --- a/vendor/google_riscv-dv/src/riscv_page_table_list.sv +++ b/vendor/google_riscv-dv/src/riscv_page_table_list.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv b/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv index 474b860c..2fc1fd00 100644 --- a/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv +++ b/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv @@ -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 diff --git a/vendor/google_riscv-dv/src/riscv_rand_instr.sv b/vendor/google_riscv-dv/src/riscv_rand_instr.sv index ce65c37d..2b269d8e 100644 --- a/vendor/google_riscv-dv/src/riscv_rand_instr.sv +++ b/vendor/google_riscv-dv/src/riscv_rand_instr.sv @@ -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 { diff --git a/vendor/google_riscv-dv/test/riscv_instr_cov_debug_test.sv b/vendor/google_riscv-dv/test/riscv_instr_cov_debug_test.sv index edd2cc2c..d38c3040 100644 --- a/vendor/google_riscv-dv/test/riscv_instr_cov_debug_test.sv +++ b/vendor/google_riscv-dv/test/riscv_instr_cov_debug_test.sv @@ -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 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 diff --git a/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv b/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv index 0f77567a..dbe5fff4 100644 --- a/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv +++ b/vendor/google_riscv-dv/test/riscv_instr_cov_test.sv @@ -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; diff --git a/vendor/google_riscv-dv/yaml/simulator.yaml b/vendor/google_riscv-dv/yaml/simulator.yaml index b47cb81c..5bf20cdd 100644 --- a/vendor/google_riscv-dv/yaml/simulator.yaml +++ b/vendor/google_riscv-dv/yaml/simulator.yaml @@ -64,7 +64,9 @@ -o design_opt" sim: cmd: > - vsim -64 -c -do /questa_sim.tcl design_opt -sv_seed + vsim -64 -c -do /questa_sim.tcl design_opt -sv_seed + cov_opts: > + -do "coverage save -onexit cov.ucdb;" - tool: dsim env_var: DSIM,DSIM_LIB_PATH diff --git a/vendor/google_riscv-dv/yaml/testlist.yaml b/vendor/google_riscv-dv/yaml/testlist.yaml index dfbdd1d7..da4e331d 100644 --- a/vendor/google_riscv-dv/yaml/testlist.yaml +++ b/vendor/google_riscv-dv/yaml/testlist.yaml @@ -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