[a-z0-9]{2,3}?) (?P[a-f0-9]+?)" \
+ " -> (?P[a-f0-9]+?)$", line)
+ if n:
+ # Write the extracted instruction to a csvcol buffer file
+ # print("%0s %0s = %0s" % (trace_instr, m.group("rd"), m.group("val")))
+ rv_instr_trace = RiscvInstructiontTraceEntry()
+ rv_instr_trace.rd = n.group("rd")
+ rv_instr_trace.rd_val = n.group("val")
+ rv_instr_trace.instr_str = trace_instr
+ rv_instr_trace.binary = trace_bin
+ rv_instr_trace.addr = trace_addr
+ trace_csv.write_trace_entry(rv_instr_trace)
+ print("Processed instruction count : %d" % instr_cnt)
+
+instr_trace = []
+# Parse input arguments
+parser = argparse.ArgumentParser()
+parser.add_argument("--log", type=str, help="Input ovpsim simulation log")
+parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
+args = parser.parse_args()
+# Process ovpsim log
+process_ovpsim_sim_log(args.log, args.csv)
diff --git a/vendor/google_riscv-dv/scripts/riscv_trace_csv.py b/vendor/google_riscv-dv/scripts/riscv_trace_csv.py
new file mode 100644
index 00000000..2fa12bbc
--- /dev/null
+++ b/vendor/google_riscv-dv/scripts/riscv_trace_csv.py
@@ -0,0 +1,112 @@
+"""
+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.
+
+Class for RISC-V instruction trace CSV
+"""
+
+import csv
+
+class RiscvInstructiontTraceEntry(object):
+ """RISC-V instruction trace entry"""
+ def __init__(self):
+ self.rd = ""
+ self.rd_val = ""
+ self.rs1 = ""
+ self.rs1_val = ""
+ self.rs2 = ""
+ self.rs2_val = ""
+ self.imm = ""
+ self.instr_name = ""
+ self.addr = ""
+ self.binary = ""
+ self.instr_str = ""
+ self.privileged_mode = ""
+
+class RiscvInstructiontTraceCsv(object):
+ """RISC-V instruction trace CSV class
+
+ This class provides functions to read/write trace CSV
+ """
+
+ def __init__(self, csv_fd):
+ self.csv_fd = csv_fd
+
+
+ def start_new_trace(self):
+ """Create a CSV file handle for a new trace"""
+ fields = ["instr", "rd", "rd_val", "rs1", "rs1_val", "rs2", "rs2_val",
+ "imm", "str", "addr", "binary", "mode"]
+ self.csv_writer = csv.DictWriter(self.csv_fd, fieldnames=fields)
+ self.csv_writer.writeheader()
+
+
+ def read_trace(self, trace):
+ """Read instruction trace from CSV file"""
+ csv_reader = csv.DictReader(self.csv_fd)
+ for row in csv_reader:
+ new_trace = RiscvInstructiontTraceEntry()
+ new_trace.rd = row['rd']
+ new_trace.rd_val = row['rd_val']
+ new_trace.addr = row['addr']
+ new_trace.binary = row['binary']
+ new_trace.instr_str = row['str']
+ trace.append(new_trace)
+
+
+ def write_trace_entry(self, entry):
+ """Write a new trace entry to CSV"""
+ self.csv_writer.writerow({'str' : entry.instr_str,
+ 'rd' : entry.rd,
+ 'rd_val' : entry.rd_val,
+ 'binary' : entry.binary,
+ 'addr' : entry.addr})
+
+def gpr_to_abi(gpr):
+ """Convert a general purpose register to its corresponding abi name"""
+ switcher = {
+ "x0" : "zero",
+ "x1" : "ra",
+ "x2" : "sp",
+ "x3" : "gp",
+ "x4" : "tp",
+ "x5" : "t0",
+ "x6" : "t1",
+ "x7" : "t2",
+ "x8" : "s0",
+ "x9" : "s1",
+ "x10" : "a0",
+ "x11" : "a1",
+ "x12" : "a2",
+ "x13" : "a3",
+ "x14" : "a4",
+ "x15" : "a5",
+ "x16" : "a6",
+ "x17" : "a7",
+ "x18" : "s2",
+ "x19" : "s3",
+ "x20" : "s4",
+ "x21" : "s5",
+ "x22" : "s6",
+ "x23" : "s7",
+ "x24" : "s8",
+ "x25" : "s9",
+ "x26" : "s10",
+ "x27" : "s11",
+ "x28" : "t3",
+ "x29" : "t4",
+ "x30" : "t5",
+ "x31" : "t6"
+ }
+ return switcher.get(gpr, "na")
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
new file mode 100644
index 00000000..fe8a0275
--- /dev/null
+++ b/vendor/google_riscv-dv/scripts/spike_log_to_trace_csv.py
@@ -0,0 +1,65 @@
+"""
+Copyright 2019 Google LLC
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+Convert spike sim log to standard riscv instruction trace format
+"""
+import re
+import argparse
+
+from riscv_trace_csv import *
+
+def process_spike_sim_log(spike_log, csv):
+ """Process SPIKE simulation log.
+
+ Extract instruction and affected register information from spike simulation
+ log and save to a list.
+ """
+ print("Processing spike log : %s" % spike_log)
+ instr_cnt = 0
+ spike_instr = ""
+
+ with open(spike_log, "r") as f, open(csv, "w") as csv_fd:
+ trace_csv = RiscvInstructiontTraceCsv(csv_fd)
+ trace_csv.start_new_trace()
+ for line in f:
+ # Extract instruction infromation
+ m = re.search(r"core(.*)\) (.*)", line)
+ if m:
+ spike_instr = m.group(2)
+ else:
+ # Extract register value information
+ m = re.search(r"(?P\d) 0x(?P[a-f0-9]+?) " \
+ "\((?P.*?)\) x\s*(?P\d*?) 0x(?P.*)", line)
+ if m:
+ # Write the extracted instruction to a csvcol buffer file
+ instr_cnt += 1
+ rv_instr_trace = RiscvInstructiontTraceEntry()
+ rv_instr_trace.rd = gpr_to_abi("x%0s" % m.group("reg"))
+ rv_instr_trace.rd_val = m.group("val")
+ rv_instr_trace.privileged_mode = m.group("pri")
+ rv_instr_trace.addr = m.group("addr")
+ rv_instr_trace.binary = m.group("bin")
+ rv_instr_trace.instr_str = spike_instr
+ trace_csv.write_trace_entry(rv_instr_trace)
+ print("Processed instruction count : %d" % instr_cnt)
+
+instr_trace = []
+# Parse input arguments
+parser = argparse.ArgumentParser()
+parser.add_argument("--log", type=str, help="Input spike simulation log")
+parser.add_argument("--csv", type=str, help="Output trace csv_buf file")
+args = parser.parse_args()
+# Process spike log
+process_spike_sim_log(args.log, args.csv)
diff --git a/vendor/google_riscv-dv/src/dv_defines.svh b/vendor/google_riscv-dv/src/dv_defines.svh
new file mode 100644
index 00000000..43098d73
--- /dev/null
+++ b/vendor/google_riscv-dv/src/dv_defines.svh
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.
+ */
+
+`ifndef uvm_object_new
+ `define uvm_object_new \
+ function new (string name=""); \
+ super.new(name); \
+ endfunction : new
+`endif
+
+`ifndef uvm_component_new
+ `define uvm_component_new \
+ function new (string name="", uvm_component parent=null); \
+ super.new(name, parent); \
+ endfunction : new
+`endif
+
+`ifndef gfn
+ `define gfn get_full_name()
+`endif
+
+`ifndef DV_CHECK
+`define DV_CHECK(T_, MSG_="", SEV_=error, ID_=`gfn) \
+ if (!(T_)) begin \
+ `uvm_``SEV_(ID_, $sformatf("Check failed (%s) %s ", `"T_`", MSG_)); \
+ end
+`endif
+
+`ifndef DV_CHECK_FATAL
+ `define DV_CHECK_FATAL(T_, MSG_="", ID_=`gfn) \
+ `DV_CHECK(T_, MSG_, fatal, ID_)
+`endif
+
+// Shorthand for common foo.randomize() + fatal check
+`ifndef DV_CHECK_RANDOMIZE_FATAL
+ `define DV_CHECK_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
+ `DV_CHECK_FATAL(VAR_.randomize(), MSG_, ID_)
+`endif
+
+// Shorthand for common std::randomize(foo) + fatal check
+`ifndef DV_CHECK_STD_RANDOMIZE_FATAL
+ `define DV_CHECK_STD_RANDOMIZE_FATAL(VAR_, MSG_="Randomization failed!", ID_=`gfn) \
+ `DV_CHECK_FATAL(std::randomize(VAR_), MSG_, ID_)
+`endif
+
+// Shorthand for common foo.randomize() with { } + fatal check
+`ifndef DV_CHECK_RANDOMIZE_WITH_FATAL
+ `define DV_CHECK_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_, MSG_="Randomization failed!", ID_=`gfn) \
+ `DV_CHECK_FATAL(VAR_.randomize() with {WITH_C_}, MSG_, ID_)
+`endif
+
+// Shorthand for common std::randomize(foo) with { } + fatal check
+`ifndef DV_CHECK_STD_RANDOMIZE_WITH_FATAL
+ `define DV_CHECK_STD_RANDOMIZE_WITH_FATAL(VAR_, WITH_C_,MSG_="Randomization failed!",ID_=`gfn) \
+ `DV_CHECK_FATAL(std::randomize(VAR_) with {WITH_C_}, MSG_, ID_)
+`endif
diff --git a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv
new file mode 100644
index 00000000..78ab6ed5
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv
@@ -0,0 +1,852 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//-----------------------------------------------------------------------------------------
+// RISC-V assembly program generator
+//
+// This is the main class to generate a complete RISC-V program, including the init routine,
+// instruction section, data section, stack section, page table, interrupt and exception
+// handling etc. Check gen_program() function to see how the program is generated.
+//-----------------------------------------------------------------------------------------
+
+class riscv_asm_program_gen extends uvm_object;
+
+ riscv_instr_gen_config cfg;
+ riscv_data_page_gen data_page_gen;
+ // User mode programs
+ riscv_instr_sequence main_program;
+ riscv_instr_sequence sub_program[];
+ // Program in binary format, stored in the data section, used to inject illegal/HINT instruction
+ riscv_instr_sequence bin_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
+ // and MPRV equal 1, kernel program can fetch/load/store from U-mode pages,
+ // smode_accessible_umode_program is designed for this purpose. There can be other cases that
+ // instruction can only be fetched from S-mode pages but load/store can access U-mode pages, or
+ // everything needs to be in S-mode pages.
+ riscv_instr_sequence smode_accessible_umode_program;
+ riscv_instr_sequence smode_program;
+ riscv_instr_sequence smode_ls_umem_program;
+ riscv_instr_stream directed_instr[];
+ string instr_stream[$];
+ riscv_callstack_gen callstack_gen;
+ riscv_privileged_common_seq privil_seq;
+ // Directed instruction ratio, occurance per 1000 instructions
+ int unsigned directed_instr_stream_ratio[string];
+ riscv_page_table_list#(SATP_MODE) page_table_list;
+
+ `uvm_object_utils(riscv_asm_program_gen)
+
+ function new (string name = "");
+ super.new(name);
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Main function to generate the whole program
+ //---------------------------------------------------------------------------------------
+
+ // This is the main function to generate all sections of the program.
+ virtual function void gen_program();
+ string sub_program_name[$];
+ instr_stream.delete();
+ // Generate program header
+ gen_program_header();
+ // Initialize general purpose registers
+ init_gpr();
+ // Create all page tables
+ create_page_table();
+ // Setup privileged mode registers and enter target privileged mode
+ pre_enter_privileged_mode();
+ // Generate sub program in binary format
+ // Illegal instruction and hint instruction cannot pass compilation, need to directly generate
+ // the instruction in binary format and store in data section to skip compilation.
+ if(cfg.enable_illegal_instruction || cfg.enable_hint_instruction) begin
+ bin_program = riscv_instr_sequence::type_id::create("bin_program");
+ bin_program.instr_cnt = cfg.bin_program_instr_cnt;
+ bin_program.label_name = bin_program.get_name();
+ bin_program.cfg = cfg;
+ if (cfg.enable_illegal_instruction) begin
+ bin_program.illegal_instr_pct = $urandom_range(5, 20);
+ end
+ if (cfg.enable_hint_instruction) begin
+ bin_program.hint_instr_pct = $urandom_range(5, 20);
+ end
+ `DV_CHECK_RANDOMIZE_FATAL(bin_program)
+ bin_program.gen_instr(.is_main_program(0), .enable_hint_instr(cfg.enable_hint_instruction));
+ bin_program.post_process_instr();
+ bin_program.generate_binary_stream(instr_binary);
+ end
+ // Init section
+ gen_init_section();
+ // Generate sub program
+ if(cfg.num_of_sub_program > 0) begin
+ sub_program = new[cfg.num_of_sub_program];
+ foreach(sub_program[i]) begin
+ sub_program[i] = riscv_instr_sequence::type_id::create($sformatf("sub_%0d",i+1));
+ sub_program[i].instr_cnt = cfg.sub_program_instr_cnt[i];
+ generate_directed_instr_stream(.label(sub_program[i].get_name()),
+ .original_instr_cnt(sub_program[i].instr_cnt),
+ .min_insert_cnt(0),
+ .instr_stream(sub_program[i].directed_instr));
+ 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_name.push_back(sub_program[i].label_name);
+ end
+ end
+ // Generate main program
+ main_program = riscv_instr_sequence::type_id::create("main_program");
+ main_program.instr_cnt = cfg.main_program_instr_cnt;
+ main_program.label_name = "_main";
+ generate_directed_instr_stream(.label("main"),
+ .original_instr_cnt(main_program.instr_cnt),
+ .min_insert_cnt(1),
+ .instr_stream(main_program.directed_instr));
+ main_program.cfg = cfg;
+ `DV_CHECK_RANDOMIZE_FATAL(main_program)
+ main_program.gen_instr(1);
+ // Setup jump instruction among main program and sub programs
+ if(cfg.num_of_sub_program != 0) begin
+ callstack_gen = riscv_callstack_gen::type_id::create("callstack_gen");
+ callstack_gen.init(cfg.num_of_sub_program+1);
+ `uvm_info(get_full_name(), "Randomizing call stack", UVM_LOW)
+ if(callstack_gen.randomize()) begin
+ program_id_t pid;
+ int idx;
+ // Insert the jump instruction based on the call stack
+ foreach(callstack_gen.program_h[i]) begin
+ foreach(callstack_gen.program_h[i].sub_program_id[j]) begin
+ idx++;
+ pid = callstack_gen.program_h[i].sub_program_id[j] - 1;
+ `uvm_info(get_full_name(), $sformatf(
+ "Gen jump instr %0d -> sub[%0d] %0d", i, j, pid+1), UVM_HIGH)
+ if(i == 0)
+ main_program.insert_jump_instr(sub_program_name[pid], idx);
+ else
+ sub_program[i-1].insert_jump_instr(sub_program_name[pid], idx);
+ end
+ end
+ end else begin
+ `uvm_fatal(get_full_name(), "Failed to generate callstack")
+ end
+ end
+ if (bin_program != null) begin
+ main_program.insert_jump_instr("sub_bin", 0);
+ end
+ main_program.post_process_instr();
+ main_program.generate_instr_stream();
+ instr_stream = {instr_stream, main_program.instr_string_list};
+ // Test done section
+ gen_test_done();
+ // Shuffle the sub programs and insert to the instruction stream
+ sub_program.shuffle();
+ foreach(sub_program[i]) begin
+ sub_program[i].post_process_instr();
+ sub_program[i].generate_instr_stream();
+ instr_stream = {instr_stream, sub_program[i].instr_string_list};
+ end
+ // 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
+ // Privileged mode switch routine
+ gen_privileged_mode_switch_routine();
+ // Program end
+ gen_program_end();
+ gen_data_page_begin();
+ // Generate the sub program in binary format
+ gen_bin_program();
+ // Page table
+ gen_page_table_section();
+ if(!cfg.no_data_page) begin
+ // Data section
+ gen_data_page();
+ end
+ gen_data_page_end();
+ // Stack section
+ gen_stack_section();
+ // Generate kernel program/data/stack section
+ gen_kernel_sections();
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Generate kernel program/data/stack sections
+ //---------------------------------------------------------------------------------------
+ virtual function void gen_kernel_sections();
+ instr_stream.push_back("_kernel_start: .align 12");
+ // Kernel programs
+ smode_accessible_umode_program = riscv_instr_sequence::type_id::
+ create("smode_accessible_umode_program");
+ smode_program = riscv_instr_sequence::type_id::create("smode_program");
+ gen_kernel_program(smode_accessible_umode_program);
+ gen_kernel_program(smode_program);
+ smode_ls_umem_program = riscv_instr_sequence::type_id::create("smode_ls_umem_program");
+ gen_kernel_program(smode_ls_umem_program);
+ // All trap/interrupt handling is in the kernel region
+ // Trap/interrupt delegation to user mode is not supported now
+ // Trap handler
+ gen_all_trap_handler();
+ // Interrupt handling subroutine
+ foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
+ gen_interrupt_handler_section(riscv_instr_pkg::supported_privileged_mode[i]);
+ end
+ // Kernel data pages
+ gen_kernel_data_page_begin();
+ if(!cfg.no_data_page) begin
+ // Data section
+ gen_data_page(.is_kernel(1'b1));
+ end
+ gen_data_page_end();
+ // Kernel stack section
+ gen_kernel_stack_section();
+ instr_stream.push_back("_kernel_end: nop");
+ endfunction
+
+ virtual function void gen_kernel_program(riscv_instr_sequence seq);
+ seq.instr_cnt = riscv_instr_pkg::kernel_program_instr_cnt;
+ generate_directed_instr_stream(.label(seq.get_name()),
+ .original_instr_cnt(seq.instr_cnt),
+ .min_insert_cnt(0),
+ .instr_stream(seq.directed_instr),
+ .access_u_mode_mem(1'b0));
+ seq.label_name = seq.get_name();
+ seq.cfg = cfg;
+ `DV_CHECK_RANDOMIZE_FATAL(seq)
+ seq.gen_instr(0);
+ seq.post_process_instr();
+ seq.generate_instr_stream();
+ instr_stream = {instr_stream, seq.instr_string_list};
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Major sections - init, stack, data, test_done etc.
+ //---------------------------------------------------------------------------------------
+
+ virtual function void gen_program_header();
+ // ------------- IBEX modification start --------------------
+ // The ibex core load the program from 0x80
+ // Some address is reserved for hardware interrupt handling, need to decide if we need to copy
+ // the init program from crt0.S later.
+ instr_stream.push_back(".macro init");
+ instr_stream.push_back(".endm");
+ instr_stream.push_back(".section .text.init");
+ instr_stream.push_back(".globl _start");
+ instr_stream.push_back("j _start");
+ // Align the start section to 0x80
+ instr_stream.push_back(".align 7");
+ instr_stream.push_back("_start:");
+ // ------------- IBEX modification end --------------------
+ endfunction
+
+ virtual function void gen_program_end();
+ // Use write_tohost to terminate spike simulation
+ gen_section("write_tohost", {"sw gp, tohost, t5"});
+ gen_section("_exit", {"j write_tohost"});
+ endfunction
+
+ virtual function void gen_data_page_begin();
+ instr_stream.push_back(".data");
+ instr_stream.push_back(".pushsection .tohost,\"aw\",@progbits;");
+ instr_stream.push_back(".align 6; .global tohost; tohost: .dword 0;");
+ instr_stream.push_back(".align 6; .global fromhost; fromhost: .dword 0;");
+ instr_stream.push_back(".popsection;");
+ instr_stream.push_back(".align 4;");
+ endfunction
+
+ virtual function void gen_kernel_data_page_begin();
+ instr_stream.push_back(".data");
+ instr_stream.push_back(".align 4;");
+ endfunction
+
+ virtual function void gen_data_page(bit is_kernel = 1'b0);
+ string data_page;
+ data_page_gen = riscv_data_page_gen::type_id::create("data_page_gen");
+ data_page_gen.cfg = cfg;
+ data_page_gen.gen_data_page(cfg.data_page_pattern, is_kernel);
+ instr_stream = {instr_stream, data_page_gen.data_page_str};
+ endfunction
+
+ virtual function void gen_data_page_end();
+ instr_stream.push_back(".align 4;");
+ endfunction
+
+ // Generate the user stack section
+ virtual function void gen_stack_section();
+ instr_stream.push_back($sformatf(".align %0d", $clog2(XLEN)));
+ instr_stream.push_back("_user_stack_start:");
+ instr_stream.push_back($sformatf(".rept %0d", riscv_instr_pkg::stack_len - 1));
+ instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
+ instr_stream.push_back(".endr");
+ instr_stream.push_back("_user_stack_end:");
+ instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
+ endfunction
+
+ // The kernal stack is used to save user program context before executing exception handling
+ virtual function void gen_kernel_stack_section();
+ instr_stream.push_back($sformatf(".align %0d", $clog2(XLEN)));
+ instr_stream.push_back("_kernel_stack_start:");
+ instr_stream.push_back($sformatf(".rept %0d", riscv_instr_pkg::kernel_stack_len - 1));
+ instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
+ instr_stream.push_back(".endr");
+ instr_stream.push_back("_kernel_stack_end:");
+ instr_stream.push_back($sformatf(".%0dbyte 0x0", XLEN/8));
+ endfunction
+
+ virtual function void gen_init_section();
+ string str;
+ 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"};
+ instr_stream.push_back(str);
+ // 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
+
+ virtual function void init_gpr();
+ string str;
+ bit [DATA_WIDTH-1:0] reg_val;
+ // Init general purpose registers with random values
+ for(int i = 0; i < 32; i++) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(reg_val,
+ reg_val dist {
+ 'h0 :/ 1,
+ 'h8000_0000 :/ 1,
+ ['h1 : 'hF] :/ 1,
+ ['h10 : 'hEFFF_FFFF] :/ 1,
+ ['hF000_0000 : 'hFFFF_FFFF] :/ 1
+ };)
+ str = $sformatf("%0sli x%0d, 0x%0x", indent, i, reg_val);
+ instr_stream.push_back(str);
+ end
+ endfunction
+
+ // Generate "test_done" section, test is finished by an ECALL instruction
+ // The ECALL trap handler will handle the clean up procedure before finishing the test.
+ virtual function void gen_test_done();
+ string str = format_string("test_done:", LABEL_STR_LEN);
+ instr_stream.push_back(str);
+ instr_stream.push_back({indent, "li gp, 1"});
+ instr_stream.push_back({indent, "ecall"});
+ endfunction
+
+ // Dump all GPR to the starting point of the program
+ // TB can check the GPR value for this memory location to compare with expected value generated
+ // by the ISA simulator. If the processor doesn't have a good tracer unit, it might not be
+ // possible to compare the GPR value after each instruction execution.
+ virtual function void gen_register_dump();
+ 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)};
+ 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)};
+ end else begin
+ str = {indent, $sformatf("sw x%0d, %0d(x%0d)", i, i*(XLEN/8), base_address_reg)};
+ end
+ instr_stream.push_back(str);
+ end
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Privileged mode entering routine
+ //---------------------------------------------------------------------------------------
+
+ virtual function void pre_enter_privileged_mode();
+ string instr[];
+ // Setup kerenal stack pointer
+ gen_section("kernel_sp", {"la tp, _kernel_stack_end"});
+ // Setup interrupt and exception delegation
+ if(!cfg.no_delegation && (cfg.init_privileged_mode != MACHINE_MODE)) begin
+ gen_delegation();
+ end
+ // Setup trap vector register
+ trap_vector_init();
+ // Initialize PTE (link page table based on their real physical address)
+ if((SATP_MODE != BARE) && (cfg.init_privileged_mode != MACHINE_MODE)) begin
+ page_table_list.process_page_table(instr);
+ gen_section("process_pt", instr);
+ end
+ // Setup mepc register, jump to init entry
+ setup_epc();
+ endfunction
+
+ virtual function void gen_privileged_mode_switch_routine();
+ privil_seq = riscv_privileged_common_seq::type_id::create("privil_seq");
+ foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
+ string instr[$];
+ if(riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
+ `uvm_info(`gfn, $sformatf("Generating privileged mode routing for %0s",
+ riscv_instr_pkg::supported_privileged_mode[i].name()), UVM_LOW)
+ // Enter privileged mode
+ privil_seq.cfg = cfg;
+ `DV_CHECK_RANDOMIZE_FATAL(privil_seq)
+ privil_seq.enter_privileged_mode(riscv_instr_pkg::supported_privileged_mode[i], instr);
+ instr_stream = {instr_stream, instr};
+ end
+ endfunction
+
+ // Setup EPC before entering target privileged mode
+ virtual function void setup_epc();
+ string instr[];
+ string mode_name;
+ instr = {"la x10, _init"};
+ if(SATP_MODE != BARE && cfg.init_privileged_mode != 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 12 bits are kept
+ // as virtual address offset.
+ instr = {instr,
+ $sformatf("slli x10, x10, %0d", XLEN - 12),
+ $sformatf("srli x10, x10, %0d", XLEN - 12)};
+ end
+ mode_name = cfg.init_privileged_mode.name();
+ instr = {instr,
+ "csrw mepc, x10",
+ $sformatf("j init_%0s", mode_name.tolower())
+ };
+ gen_section("mepc_setup", instr);
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Privileged CSR setup for interrupt and exception handling and delegation
+ //---------------------------------------------------------------------------------------
+
+ // Interrupt and exception delegation setting.
+ // The lower level exception and interrupt can be delegated to higher level handler.
+ virtual function void gen_delegation();
+ gen_delegation_instr(MEDELEG, MIDELEG,
+ cfg.m_mode_exception_delegation,
+ cfg.m_mode_interrupt_delegation);
+ if(riscv_instr_pkg::support_umode_trap) begin
+ gen_delegation_instr(SEDELEG, SIDELEG,
+ cfg.s_mode_exception_delegation,
+ cfg.s_mode_interrupt_delegation);
+ end
+ endfunction
+
+ virtual function void gen_delegation_instr(privileged_reg_t edeleg,
+ privileged_reg_t ideleg,
+ bit edeleg_enable[exception_cause_t],
+ bit ideleg_enable[interrupt_cause_t]);
+ bit [XLEN-1:0] deleg_val;
+ string section_name;
+ string instr[];
+ // Exception delegation setup
+ foreach(edeleg_enable[cause]) begin
+ if(edeleg_enable[cause]) begin
+ 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())};
+ // Interrupt delegation setup
+ deleg_val = '0;
+ foreach(ideleg_enable[cause]) begin
+ if(ideleg_enable[cause]) begin
+ deleg_val = deleg_val | (1 << int'(cause));
+ end
+ end
+ instr = {instr,
+ $sformatf("li a0, 0x%0x", deleg_val),
+ $sformatf("csrw 0x%0x, a0 # %0s", ideleg, ideleg.name())};
+ section_name = edeleg.name();
+ section_name = section_name.tolower();
+ gen_section($sformatf("%0s_setup", section_name), instr);
+ endfunction
+
+ // Setup trap vector - MTVEC, STVEC, UTVEC
+ virtual function void trap_vector_init();
+ string instr[];
+ privileged_reg_t trap_vec_reg;
+ string tvec_name;
+ foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
+ case(riscv_instr_pkg::supported_privileged_mode[i])
+ MACHINE_MODE: trap_vec_reg = MTVEC;
+ SUPERVISOR_MODE: trap_vec_reg = STVEC;
+ USER_MODE: trap_vec_reg = UTVEC;
+ endcase
+ // Skip utvec init if trap delegation to u_mode is not supported
+ // TODO: For now the default mode is direct mode, needs to support vector mode
+ if((riscv_instr_pkg::supported_privileged_mode[i] == USER_MODE) && !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())};
+ 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)};
+ end
+ instr = {instr, $sformatf("csrw 0x%0x, a0 # %0s", trap_vec_reg, trap_vec_reg.name())};
+ end
+ gen_section("trap_vec_init", instr);
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Exception handling routine
+ //---------------------------------------------------------------------------------------
+
+ // Trap handling routine
+ virtual function void gen_all_trap_handler();
+ string instr[$];
+ foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
+ if(riscv_instr_pkg::supported_privileged_mode[i] < cfg.init_privileged_mode) continue;
+ case(riscv_instr_pkg::supported_privileged_mode[i])
+ MACHINE_MODE:
+ gen_trap_handler_section("m", MCAUSE, MTVEC, MTVAL, MEPC, MSCRATCH, MSTATUS);
+ SUPERVISOR_MODE:
+ gen_trap_handler_section("s", SCAUSE, STVEC, STVAL, SEPC, SSCRATCH, SSTATUS);
+ USER_MODE:
+ if(riscv_instr_pkg::support_umode_trap)
+ gen_trap_handler_section("u", UCAUSE, UTVEC, UTVAL, UEPC, USCRATCH, USTATUS);
+ endcase
+ end
+ // Ecall handler
+ gen_ecall_handler();
+ // Illegal instruction handler
+ gen_illegal_instr_handler();
+ // Generate page table fault handling routine
+ // Page table fault is always handled in machine mode, as virtual address translation may be
+ // broken when page fault happens.
+ if(page_table_list != null) begin
+ page_table_list.gen_page_fault_handling_routine(instr);
+ end else begin
+ instr = {"nop"};
+ end
+ gen_section("pt_fault_handler", instr);
+ endfunction
+
+ // Generate the interrupt and trap handler for different privileged mode.
+ // The trap handler checks the xCAUSE to determine the type of the exception and jumps to
+ // corresponding exeception handling routine.
+ virtual function void gen_trap_handler_section(string mode,
+ privileged_reg_t cause, privileged_reg_t tvec,
+ privileged_reg_t tval, privileged_reg_t epc,
+ privileged_reg_t scratch, privileged_reg_t status);
+ bit is_interrupt = 'b1;
+ string tvec_name;
+ string instr[$];
+ // 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);
+ 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),
+ // The trap is caused by an exception, read back xCAUSE, xEPC, xSTATUS 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 x29, 0x%0x # %0s", status, status.name()),
+ // 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",
+ // Page table fault or access fault conditions
+ $sformatf("li a2, 0x%0x", INSTRUCTION_ACCESS_FAULT),
+ "beq a1, a2, pt_fault_handler",
+ $sformatf("li a2, 0x%0x", LOAD_ACCESS_FAULT),
+ "beq a1, a2, pt_fault_handler",
+ $sformatf("li a2, 0x%0x", STORE_AMO_ACCESS_FAULT),
+ "beq a1, a2, pt_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",
+ // Illegal instruction exception
+ $sformatf("li a2, 0x%0x # ILLEGAL_INSTRUCTION", ILLEGAL_INSTRUCTION),
+ "beq a1, a2, illegal_instr_handler",
+ // Skip checking tval for illegal instruction as it's implementation specific
+ $sformatf("csrr x30, 0x%0x # %0s", tval, tval.name()),
+ "1: jal x1, test_done "
+ };
+
+ // The trap handler will occupy one 4KB page, it will be allocated one entry in the page table
+ // with a specific privileged mode.
+ instr_stream.push_back(".align 12");
+ tvec_name = tvec.name();
+ gen_section($sformatf("%0s_handler", tvec_name.tolower()), instr);
+
+ endfunction
+
+ // ECALL trap handler
+ // It does some clean up like dump GPRs before communicating with host to terminate the test.
+ // User can extend this function if some custom clean up routine is needed.
+ virtual function void gen_ecall_handler();
+ string str;
+ str = format_string("ecall_handler:", LABEL_STR_LEN);
+ instr_stream.push_back(str);
+ dump_perf_stats();
+ gen_register_dump();
+ str = format_string(" ", LABEL_STR_LEN);
+ str = {str, "j write_tohost"};
+ instr_stream.push_back(str);
+ endfunction
+
+ // Illegal instruction handler
+ // Note: Save the illegal instruction to MTVAL is optional in the spec, and mepc could be
+ // a virtual address that cannot be used in machine mode handler. As a result, there's no way to
+ // know the illegal instruction is compressed or not. This hanlder just simply adds the PC by
+ // 4 and resumes execution. The way that the illegal instruction is injected guarantees that
+ // PC + 4 is a valid instruction boundary.
+ virtual function void gen_illegal_instr_handler();
+ string str;
+ string instr[$] = {
+ "csrr x31, mepc",
+ "addi x31, x31, 4",
+ "csrw mepc, x31"
+ };
+ pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
+ instr.push_back("mret");
+ gen_section("illegal_instr_handler", instr);
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Page table setup
+ //---------------------------------------------------------------------------------------
+
+ // Create page table if virtual address translation is supported.
+ // The page is created based on the address translation mode - SV32, SV39, SV48
+ // Right now only the lowest level 4KB page table is configured as leaf page table entry (PTE),
+ // all the other super pages are link PTE.
+ virtual function void create_page_table();
+ string instr[];
+ if((SATP_MODE != BARE) && (cfg.init_privileged_mode != MACHINE_MODE)) begin
+ page_table_list = riscv_page_table_list#(SATP_MODE)::
+ type_id::create("page_table_list");
+ page_table_list.cfg = cfg;
+ page_table_list.create_page_table_list();
+ page_table_list.enable_exception = cfg.enable_page_table_exception;
+ `uvm_info(`gfn, $sformatf("Randomizing page tables, totally %0d page tables, mode = %0s",
+ page_table_list.page_table.size(), cfg.init_privileged_mode.name()), UVM_LOW)
+ page_table_list.privileged_mode = cfg.init_privileged_mode;
+ `DV_CHECK_RANDOMIZE_FATAL(page_table_list);
+ page_table_list.randomize_page_table();
+ `uvm_info(`gfn, "Finished creating page tables", UVM_LOW)
+ end
+ endfunction
+
+ // Generate the page table section of the program
+ // The page table is generated as a group of continuous 4KB data sections.
+ virtual function void gen_page_table_section();
+ string page_table_section[$];
+ if(page_table_list != null) begin
+ foreach(page_table_list.page_table[i]) begin
+ page_table_list.page_table[i].gen_page_table_section(page_table_section);
+ instr_stream = {instr_stream, page_table_section};
+ end
+ end
+ endfunction
+
+ // Only extend this function if the core utilizes a PLIC for handling interrupts
+ // In this case, the core will write to a specific location as the response to the interrupt, and
+ // external PLIC unit can detect this response and process the interrupt clean up accordingly.
+ virtual function void gen_plic_section(ref string interrupt_handler_instr[$]);
+ endfunction
+
+ // Interrupt handler routine
+ virtual function void gen_interrupt_handler_section(privileged_mode_t mode);
+ string mode_prefix;
+ string ls_unit;
+ privileged_reg_t status, ip, ie, scratch;
+ string interrupt_handler_instr[$];
+ ls_unit = (XLEN == 32) ? "w" : "d";
+ if(mode < cfg.init_privileged_mode) return;
+ case(mode)
+ MACHINE_MODE: begin
+ mode_prefix = "m";
+ status = MSTATUS;
+ ip = MIP;
+ ie = MIE;
+ scratch = MSCRATCH;
+ end
+ SUPERVISOR_MODE: begin
+ mode_prefix = "s";
+ status = SSTATUS;
+ ip = SIP;
+ ie = SIE;
+ scratch = SSCRATCH;
+ end
+ USER_MODE: begin
+ mode_prefix = "u";
+ status = USTATUS;
+ ip = UIP;
+ ie = UIE;
+ scratch = USCRATCH;
+ end
+ default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode: %0s", mode.name()))
+ endcase
+ // Read back interrupt related privileged CSR
+ // 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()),
+ // Clean all the pending interrupt
+ $sformatf("csrrc a1, 0x%0x, a1 # %0s;", ip, 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);
+ interrupt_handler_instr = {interrupt_handler_instr,
+ $sformatf("%0sret;", mode_prefix)
+ };
+ // The interrupt handler will use one 4KB page
+ instr_stream.push_back(".align 12");
+ gen_section($sformatf("%0smode_intr_handler", mode_prefix), interrupt_handler_instr);
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Helper functions
+ //---------------------------------------------------------------------------------------
+
+ // Generate a code section
+ virtual function void gen_section(string label, string instr[$]);
+ string str;
+ if(label != "") begin
+ str = format_string($sformatf("%0s:", label), LABEL_STR_LEN);
+ instr_stream.push_back(str);
+ end
+ foreach(instr[i]) begin
+ str = {indent, instr[i]};
+ instr_stream.push_back(str);
+ end
+ instr_stream.push_back("");
+ endfunction
+
+ // Dump performance CSRs if applicable
+ virtual function void dump_perf_stats();
+ endfunction
+
+ // Write the generated program to a file
+ function void gen_test_file(string test_name);
+ int fd;
+ fd = $fopen(test_name,"w");
+ foreach(instr_stream[i]) begin
+ $fwrite(fd, {instr_stream[i],"\n"});
+ end
+ $fclose(fd);
+ `uvm_info(get_full_name(), $sformatf("%0s is generated", test_name), UVM_LOW)
+ endfunction
+
+ //---------------------------------------------------------------------------------------
+ // Inject directed instruction stream
+ //---------------------------------------------------------------------------------------
+
+ virtual function void add_directed_instr_stream(string name, int unsigned ratio);
+ directed_instr_stream_ratio[name] = ratio;
+ endfunction
+
+ // Generate directed instruction stream based on the ratio setting
+ virtual function void generate_directed_instr_stream(input string label,
+ input int unsigned original_instr_cnt,
+ input int unsigned min_insert_cnt = 0,
+ input bit access_u_mode_mem = 1,
+ output riscv_instr_stream instr_stream[]);
+ uvm_object object_h;
+ riscv_rand_instr_stream new_instr_stream;
+ int unsigned instr_insert_cnt;
+ int unsigned idx;
+ uvm_coreservice_t coreservice = uvm_coreservice_t::get();
+ uvm_factory factory = coreservice.get_factory();
+ if(cfg.no_directed_instr) return;
+ foreach(directed_instr_stream_ratio[instr_stream_name]) begin
+ instr_insert_cnt = original_instr_cnt * directed_instr_stream_ratio[instr_stream_name] / 1000;
+ if(instr_insert_cnt <= min_insert_cnt) begin
+ instr_insert_cnt = min_insert_cnt;
+ end
+ `uvm_info(get_full_name(), $sformatf("Insert directed instr stream %0s %0d/%0d times",
+ instr_stream_name, instr_insert_cnt, original_instr_cnt), UVM_LOW)
+ for(int i = 0; i < instr_insert_cnt; i++) begin
+ string name = $sformatf("%0s_%0d", instr_stream_name, i);
+ object_h = factory.create_object_by_name(instr_stream_name, get_full_name(), name);
+ if(object_h == null) begin
+ `uvm_fatal(get_full_name(), $sformatf("Cannot create instr stream %0s", name))
+ end
+ if($cast(new_instr_stream, object_h)) begin
+ new_instr_stream.cfg = cfg;
+ new_instr_stream.label = $sformatf("%0s_instr_%0d", label, idx);
+ new_instr_stream.access_u_mode_mem = access_u_mode_mem;
+ `DV_CHECK_RANDOMIZE_FATAL(new_instr_stream)
+ instr_stream = {instr_stream, new_instr_stream};
+ end else begin
+ `uvm_fatal(get_full_name(), $sformatf("Cannot cast instr stream %0s", name))
+ end
+ idx++;
+ end
+ end
+ instr_stream.shuffle();
+ endfunction
+
+ // Generate sub-program in binary format, this is needed for illegal and HINT instruction
+ function void gen_bin_program();
+ if (bin_program != null) begin
+ string str;
+ instr_stream.push_back("instr_bin:");
+ instr_stream.push_back(".align 12");
+ foreach (instr_binary[i]) begin
+ if (((i+1) % 8 == 0) || (i == instr_binary.size() - 1)) begin
+ if (str != "")
+ instr_stream.push_back($sformatf(".word %0s, %0s", str, instr_binary[i]));
+ else
+ instr_stream.push_back($sformatf(".word %0s", instr_binary[i]));
+ str = "";
+ end else begin
+ if (str != "") begin
+ str = {str, ", ", instr_binary[i]};
+ end else begin
+ str = instr_binary[i];
+ end
+ end
+ end
+ end
+ endfunction
+
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_callstack_gen.sv b/vendor/google_riscv-dv/src/riscv_callstack_gen.sv
new file mode 100644
index 00000000..31f42354
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_callstack_gen.sv
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//-----------------------------------------------------------------------------------------
+// RISC-V abstract program class
+//
+// This class is used to model a node in a callstack tree, no actual instruction is included
+// in this class.
+//-----------------------------------------------------------------------------------------
+
+class riscv_program extends uvm_object;
+
+ // ID of the current program
+ rand program_id_t program_id;
+ // The level of current program in the call stack tree
+ rand int unsigned call_stack_level;
+ // The list of all sub-programs of the current program
+ rand program_id_t sub_program_id[];
+
+ constraint legal_c {
+ unique{sub_program_id};
+ foreach(sub_program_id[i]) {
+ // Cannot call itself, recursive function call is not supported
+ sub_program_id[i] != program_id;
+ }
+ }
+
+ `uvm_object_utils(riscv_program)
+
+ function new (string name = "");
+ super.new(name);
+ endfunction
+
+ function string convert2string();
+ string str = $sformatf("PID[%0d] Lv[%0d] :", program_id, call_stack_level);
+ foreach(sub_program_id[i])
+ str = $sformatf("%0s %0d", str, sub_program_id[i]);
+ return str;
+ endfunction
+
+endclass
+
+//-----------------------------------------------------------------------------------------
+// RISC-V assembly program call stack generator
+//
+// The call stack is generated as a tree structure to avoid dead call loop.
+// Level 0: P0
+// / | \
+// Level 1: P1 P2 P3
+// | \/ \
+// Level 2: P4 P5 P6
+// |
+// Level 3: P7
+//
+// Rules: A program can only call the program in the next level.
+// A program can be called many times by other upper level programs.
+// A program can call the same lower level programs multiple times.
+//-----------------------------------------------------------------------------------------
+
+class riscv_callstack_gen extends uvm_object;
+
+ // Number of programs in the call stack
+ int program_cnt = 10;
+ // Handles of all programs
+ riscv_program program_h[];
+ // Maximum call stack level
+ int max_stack_level = 50;
+ // Call stack level of each program
+ rand bit[10:0] stack_level[];
+
+ constraint program_stack_level_c {
+ stack_level.size() == program_cnt;
+ // The stack level is assigned in ascending order to avoid call loop
+ stack_level[0] == 0;
+ foreach(stack_level[i]) {
+ if(i > 0) {
+ stack_level[i] inside {[1:program_cnt-1]};
+ stack_level[i] >= stack_level[i-1];
+ stack_level[i] <= stack_level[i-1]+1;
+ stack_level[i] <= max_stack_level;
+ }
+ }
+ }
+
+ `uvm_object_utils(riscv_callstack_gen)
+
+ function new (string name = "");
+ super.new(name);
+ endfunction
+
+ // Init all program instances before randomization
+ function void init(int program_cnt);
+ this.program_cnt = program_cnt;
+ program_h = new[program_cnt];
+ foreach(program_h[i])
+ program_h[i] = riscv_program::type_id::create($sformatf("program_%0d", i));
+ endfunction
+
+ // In the randomiation stage, only the stack level of each program is specified. The call stack
+ // generation process is to build the call relationship between different programs. This is
+ // implemented with post randomize rather than constraints for performance considerations.
+ // Solving a complex call stack with SV constraint could take considerable time for the solver.
+ function void post_randomize();
+ int last_level = stack_level[program_cnt-1];
+ foreach(program_h[i]) begin
+ program_h[i].program_id = i;
+ program_h[i].call_stack_level = stack_level[i];
+ end
+ // Top-down generate the entire call stack.
+ // A program can only call the programs in the next level.
+ for(int i = 0; i < last_level; i ++) begin
+ int total_sub_program_cnt;
+ int program_list[$];
+ int next_program_list[$];
+ int sub_program_id_pool[];
+ int sub_program_cnt[];
+ int idx;
+ program_list = stack_level.find_index() with (item == i);
+ next_program_list = stack_level.find_index() with (item == i+1);
+ // Randmly duplicate some sub programs in the pool to create a case that
+ // one sub program is called by multiple caller. Also it's possible to call
+ // the same sub program in one program multiple times.
+ total_sub_program_cnt = $urandom_range(next_program_list.size(),
+ next_program_list.size() + 1);
+ sub_program_id_pool = new[total_sub_program_cnt];
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(sub_program_id_pool,
+ foreach(sub_program_id_pool[j]) {
+ if(j < next_program_list.size()) {
+ sub_program_id_pool[j] == next_program_list[j];
+ } else {
+ sub_program_id_pool[j] inside {next_program_list};
+ }
+ })
+ sub_program_id_pool.shuffle();
+ sub_program_cnt = new[program_list.size()];
+ `uvm_info(get_full_name(), $sformatf("%0d programs @Lv%0d-> %0d programs at next level",
+ program_list.size(), i, sub_program_id_pool.size()), UVM_HIGH)
+ // Distribute the programs of the next level among the programs of current level
+ // Make sure all program has a caller so that no program is obsolete.
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(sub_program_cnt,
+ sub_program_cnt.sum() == sub_program_id_pool.size();
+ foreach(sub_program_cnt[j]) {
+ sub_program_cnt[j] inside {[0: sub_program_id_pool.size()]};
+ })
+ foreach(program_list[j]) begin
+ int id = program_list[j];
+ program_h[id].sub_program_id = new[sub_program_cnt[j]];
+ `uvm_info(get_full_name(), $sformatf("%0d sub programs are assigned to program[%0d]",
+ sub_program_cnt[j], id), UVM_HIGH)
+ foreach(program_h[id].sub_program_id[k]) begin
+ program_h[id].sub_program_id[k] = sub_program_id_pool[idx];
+ idx++;
+ end
+ end
+ end
+ endfunction
+
+ function void print_call_stack(program_id_t i, string str);
+ if(program_h[i].sub_program_id.size() == 0)
+ `uvm_info(get_full_name(), str, UVM_LOW)
+ else begin
+ foreach(program_h[i].sub_program_id[j]) begin
+ print_call_stack(program_h[i].sub_program_id[j],
+ $sformatf("%0s -> %0d", str, program_h[i].sub_program_id[j]));
+ end
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_core_setting.sv b/vendor/google_riscv-dv/src/riscv_core_setting.sv
new file mode 100644
index 00000000..c523573c
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_core_setting.sv
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2019 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//-----------------------------------------------------------------------------
+// Processor feature configuration
+//-----------------------------------------------------------------------------
+// XLEN
+parameter int XLEN = 32;
+
+// Parameter for SATP mode, set to BARE if address translation is not supported
+parameter satp_mode_t SATP_MODE = BARE;
+
+// Supported Privileged mode
+privileged_mode_t supported_privileged_mode[] = {MACHINE_MODE};
+
+// Unsupported instructions
+// Avoid generating these instructions in regular regression
+// FENCE.I is intentionally treated as illegal instruction by ibex core
+riscv_instr_name_t unsupported_instr[] = {FENCEI};
+
+// ISA supported by the processor
+riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV32C};
+
+// Support delegate trap to user mode
+bit support_umode_trap = 0;
+
+// Support sfence.vma instruction
+bit support_sfence = 0;
+
+// Cache line size (in bytes)
+// If processor does not support caches, set to XLEN/8
+int dcache_line_size_in_bytes = 128;
+
+// Number of data section
+// For processor that doesn't have data TLB, this can be set to 1
+// For processor that supports data TLB, this should be set to be larger than the number
+// of entries of dTLB to cover dTLB hit/miss scenario
+int num_of_data_pages = 4;
+
+// Data section byte size
+// For processor with no dTLB and data cache, keep the value below 10K
+// For processor with dTLB support, set it to the physical memory size that covers one entry
+// of the dTLB
+int data_page_size = 4096;
+int data_page_alignment = $clog2(data_page_size);
+
+// Stack section word length
+int stack_len = 5000;
+
+//-----------------------------------------------------------------------------
+// Kernel section setting, used by supervisor mode programs
+//-----------------------------------------------------------------------------
+
+// Number of kernel data pages
+int num_of_kernel_data_pages = 2;
+
+// Byte size of kernel data pages
+int kernel_data_page_size = 4096;
+
+// Kernel Stack section word length
+int kernel_stack_len = 5000;
+
+// Number of instructions for each kernel program
+int kernel_program_instr_cnt = 400;
diff --git a/vendor/google_riscv-dv/src/riscv_data_page_gen.sv b/vendor/google_riscv-dv/src/riscv_data_page_gen.sv
new file mode 100644
index 00000000..bb0c67f1
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_data_page_gen.sv
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//-----------------------------------------------------------------------------------------
+// RISC-V assmebly program data section generator
+// There can be user mode and supervisor(kernel) mode data pages
+//-----------------------------------------------------------------------------------------
+
+class riscv_data_page_gen extends uvm_object;
+
+ riscv_instr_gen_config cfg;
+ string data_page_str[$];
+
+ `uvm_object_utils(riscv_data_page_gen)
+
+ function new (string name = "");
+ super.new(name);
+ endfunction
+
+ // The data section can be initialized with different data pattern:
+ // - Random value, incremental value, all zeros
+ virtual function void gen_data(input int idx,
+ input data_pattern_t pattern,
+ input int unsigned num_of_bytes,
+ output bit [7:0] data[]);
+ bit [7:0] temp_data;
+ data = new[num_of_bytes];
+ foreach(data[i]) begin
+ if(pattern == RAND_DATA) begin
+ `DV_CHECK_STD_RANDOMIZE_FATAL(temp_data)
+ data[i] = temp_data;
+ end else if (pattern == INCR_VAL) begin
+ data[i] = (idx + i) % 256;
+ end
+ end
+ endfunction
+
+ // Generate the assembly code for the data section
+ function void gen_data_page(data_pattern_t pattern, bit is_kernel = 1'b0);
+ string tmp_str;
+ bit [7:0] tmp_data[];
+ int page_cnt;
+ int page_size;
+ data_page_str = {};
+ page_cnt = is_kernel ? riscv_instr_pkg::num_of_kernel_data_pages :
+ riscv_instr_pkg::num_of_data_pages;
+ page_size = is_kernel ? riscv_instr_pkg::kernel_data_page_size :
+ riscv_instr_pkg::data_page_size;
+ for(int section_idx = 0; section_idx < page_cnt; section_idx++) begin
+ if(is_kernel) begin
+ data_page_str.push_back($sformatf("kernel_data_page_%0d:", section_idx));
+ end else begin
+ data_page_str.push_back($sformatf("data_page_%0d:", section_idx));
+ end
+ data_page_str.push_back($sformatf(".align %0d", riscv_instr_pkg::data_page_alignment));
+ for(int i = 0; i < page_size; i+= 32) begin
+ gen_data(.idx(i), .pattern(pattern), .num_of_bytes(32), .data(tmp_data));
+ tmp_str = format_string($sformatf(".word %0s", format_data(tmp_data)), LABEL_STR_LEN);
+ data_page_str.push_back(tmp_str);
+ end
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_defines.svh b/vendor/google_riscv-dv/src/riscv_defines.svh
new file mode 100644
index 00000000..fe3c7fff
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_defines.svh
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2018 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 add_instr(instr_n, instr_format, instr_category, instr_group, imm_tp = IMM) \
+ constraint riscv_``instr_group``_``instr_n``_c { \
+ if (instr_name == ``instr_n) { \
+ format == ``instr_format; \
+ category == ``instr_category; \
+ group == ``instr_group; \
+ imm_type == ``imm_tp; \
+ } \
+ }
+
+`define add_pseudo_instr(instr_n, instr_format, instr_category, instr_group) \
+ constraint riscv_``instr_group``_``instr_n``_c { \
+ if (pseudo_instr_name == ``instr_n) { \
+ format == ``instr_format; \
+ category == ``instr_category; \
+ group == ``instr_group; \
+ } \
+ }
+
diff --git a/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv
new file mode 100644
index 00000000..4b4ec705
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_directed_instr_lib.sv
@@ -0,0 +1,437 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// Base class for directed instruction stream
+class riscv_directed_instr_stream extends riscv_rand_instr_stream;
+
+ `uvm_object_utils(riscv_directed_instr_stream)
+
+ string label;
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ function void post_randomize();
+ foreach(instr_list[i]) begin
+ instr_list[i].has_label = 1'b0;
+ instr_list[i].atomic = 1'b1;
+ end
+ instr_list[0].comment = $sformatf("Start %0s", get_name());
+ instr_list[$].comment = $sformatf("End %0s", get_name());
+ if(label!= "") begin
+ instr_list[0].label = label;
+ instr_list[0].has_label = 1'b1;
+ end
+ endfunction
+
+endclass
+
+// Create a infinte zero instruction loop, test if we can interrupt or
+// enter debug mode while core is executing this loop
+class riscv_infinte_loop_instr extends riscv_directed_instr_stream;
+
+ string label_prefix = "";
+ string label_name;
+
+ `uvm_object_utils(riscv_infinte_loop_instr)
+
+ constraint instr_c {
+ foreach(instr_list[i]) {
+ instr_list[i].imm == 0;
+ instr_list[i].category inside {JUMP, BRANCH};
+ instr_list[i].instr_name != JALR;
+ }
+ }
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ function void pre_randomize();
+ riscv_rand_instr instr;
+ initialize_instr_list(5);
+ foreach(instr_list[i]) begin
+ $cast(instr, instr_list[i]);
+ instr.constraint_cfg_knob_c.constraint_mode(0);
+ end
+ endfunction
+
+ function void post_randomize();
+ foreach(instr_list[i]) begin
+ label_name = $sformatf("%0s_inf_loop_%0d", label_prefix, i);
+ instr_list[i].atomic = 1'b1;
+ instr_list[i].label = label_name;
+ instr_list[i].imm_str = label_name;
+ instr_list[i].has_label = 1'b1;
+ instr_list[i].branch_assigned = 1'b1;
+ end
+ endfunction
+
+endclass
+
+// Jump instruction (JAL, JALR)
+// la rd0, jump_tagert_label
+// addi rd1, offset, rd0
+// jalr rd, offset, rd1
+// For JAL, restore the stack before doing the jump
+class riscv_jump_instr extends riscv_rand_instr_stream;
+
+ rand riscv_instr_base jump;
+ rand riscv_instr_base addi;
+ rand riscv_pseudo_instr la;
+ rand riscv_instr_base branch;
+ rand bit enable_branch;
+ rand int mixed_instr_cnt;
+ riscv_instr_base stack_exit_instr[];
+ string target_program_label;
+ int idx;
+
+ constraint instr_c {
+ solve jump.instr_name before addi.imm;
+ solve jump.instr_name before addi.rs1;
+ jump.instr_name dist {JAL := 1, JALR := 1};
+ jump.rd == RA;
+ !(addi.rs1 inside {cfg.reserved_regs, ZERO});
+ addi.rs1 == la.rd;
+ addi.rd == la.rd;
+ // Avoid using negative offset -1024
+ addi.imm != 'hFFFF_FC00;
+ jump.imm == ~addi.imm + 1;
+ jump.rs1 == addi.rd;
+ addi.instr_name == ADDI;
+ branch.category == BRANCH;
+ la.pseudo_instr_name == LA;
+ soft mixed_instr_cnt inside {[5:10]};
+ }
+
+ `uvm_object_utils(riscv_jump_instr)
+
+ function new(string name = "");
+ super.new(name);
+ jump = riscv_instr_base::type_id::create("jump");
+ la = riscv_pseudo_instr::type_id::create("la");
+ addi = riscv_instr_base::type_id::create("addi");
+ branch = riscv_instr_base::type_id::create("branch");
+ instr_list.rand_mode(0);
+ endfunction
+
+ function void post_randomize();
+ riscv_instr_base instr[];
+ // Generate some random instructions to mix with jump instructions
+ reserved_rd = {addi.rs1};
+ initialize_instr_list(mixed_instr_cnt);
+ gen_instr(1'b1);
+ la.imm_str = target_program_label;
+ // The branch instruction is always inserted right before the jump instruction to avoid
+ // skipping other required instructions like restore stack, load jump base etc.
+ // The purse of adding the branch instruction here is to cover branch -> jump scenario.
+ if(enable_branch) instr = {branch};
+ // Restore stack before unconditional jump
+ if(jump.rd == ZERO) begin
+ instr= {stack_exit_instr, instr};
+ end
+ if(jump.instr_name == JAL) begin
+ jump.imm_str = target_program_label;
+ end else begin
+ instr = {la, addi, instr};
+ end
+ mix_instr_stream(instr);
+ instr_list = {instr_list, jump};
+ foreach(instr_list[i]) begin
+ instr_list[i].has_label = 1'b0;
+ instr_list[i].atomic = 1'b1;
+ end
+ jump.has_label = 1'b1;
+ jump.label = $sformatf("j_%0s_%0s_%0d", label, target_program_label, idx);
+ branch.imm_str = jump.label;
+ branch.comment = "branch to jump instr";
+ branch.branch_assigned = 1'b1;
+ endfunction
+endclass
+
+// Push stack instruction stream
+class riscv_push_stack_instr extends riscv_rand_instr_stream;
+
+ int stack_len;
+ int num_of_reg_to_save;
+ int num_of_redudant_instr;
+ riscv_instr_base push_stack_instr[];
+ riscv_reg_t saved_regs[];
+ rand riscv_instr_base branch_instr;
+ rand bit enable_branch;
+ string push_start_label;
+
+ `uvm_object_utils(riscv_push_stack_instr)
+
+ function new(string name = "");
+ super.new(name);
+ branch_instr = riscv_instr_base::type_id::create("branch_instr");
+ endfunction
+
+ function void init();
+ // Save RA, T0 and all reserved loop regs
+ saved_regs = {RA, T0, cfg.loop_regs};
+ num_of_reg_to_save = saved_regs.size();
+ if(num_of_reg_to_save * (XLEN/8) > stack_len) begin
+ `uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
+ stack_len, num_of_reg_to_save))
+ end
+ num_of_redudant_instr = $urandom_range(3,10);
+ initialize_instr_list(num_of_redudant_instr);
+ endfunction
+
+ virtual function void gen_push_stack_instr(int stack_len, bit allow_branch = 1);
+ this.stack_len = stack_len;
+ init();
+ gen_instr(1'b1);
+ push_stack_instr = new[num_of_reg_to_save+1];
+ foreach(push_stack_instr[i]) begin
+ push_stack_instr[i] = riscv_instr_base::type_id::
+ create($sformatf("push_stack_instr_%0d", i));
+ end
+ // addi sp,sp,-imm
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[0],
+ instr_name == ADDI; rd == SP; rs1 == 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);)
+ 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);)
+ end
+ push_stack_instr[i+1].process_load_store = 0;
+ end
+ if (allow_branch) begin
+ `DV_CHECK_STD_RANDOMIZE_FATAL(enable_branch)
+ end else begin
+ enable_branch = 0;
+ end
+ if(enable_branch) begin
+ // Cover jal -> branch scenario, the branch is added before push stack operation
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
+ category == BRANCH;)
+ branch_instr.imm_str = push_start_label;
+ branch_instr.branch_assigned = 1'b1;
+ push_stack_instr[0].label = push_start_label;
+ push_stack_instr[0].has_label = 1'b1;
+ push_stack_instr = {branch_instr, push_stack_instr};
+ end
+ mix_instr_stream(push_stack_instr);
+ foreach(instr_list[i]) begin
+ instr_list[i].atomic = 1'b1;
+ if(instr_list[i].label == "")
+ instr_list[i].has_label = 1'b0;
+ end
+ endfunction
+
+endclass
+
+// Pop stack instruction stream
+class riscv_pop_stack_instr extends riscv_rand_instr_stream;
+
+ int stack_len;
+ int num_of_reg_to_save;
+ int num_of_redudant_instr;
+ riscv_instr_base pop_stack_instr[];
+ riscv_reg_t saved_regs[];
+
+ `uvm_object_utils(riscv_pop_stack_instr)
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ function void init();
+ num_of_reg_to_save = saved_regs.size();
+ if(num_of_reg_to_save * 4 > stack_len) begin
+ `uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
+ stack_len, num_of_reg_to_save))
+ end
+ num_of_redudant_instr = $urandom_range(3,10);
+ initialize_instr_list(num_of_redudant_instr);
+ endfunction
+
+ virtual function void gen_pop_stack_instr(int stack_len, riscv_reg_t saved_regs[]);
+ this.stack_len = stack_len;
+ this.saved_regs = saved_regs;
+ init();
+ gen_instr(1'b1);
+ pop_stack_instr = new[num_of_reg_to_save+1];
+ foreach(pop_stack_instr[i]) begin
+ pop_stack_instr[i] = riscv_instr_base::type_id::
+ create($sformatf("pop_stack_instr_%0d", i));
+ end
+ 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);)
+ 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);)
+ 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;)
+ 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
+ instr_list[i].atomic = 1'b1;
+ instr_list[i].has_label = 1'b0;
+ end
+ endfunction
+
+endclass
+
+// Cover the long fprward and backward jump
+class riscv_long_branch_instr extends riscv_rand_instr_stream;
+
+ int branch_instr_stream_len = 100;
+ int branch_instr_offset = 999;
+ riscv_rand_instr_stream forward_branch_instr_stream;
+ riscv_rand_instr_stream backward_branch_instr_stream;
+ riscv_instr_base jump_instr;
+
+ `uvm_object_utils(riscv_long_branch_instr)
+
+ function new(string name = "");
+ super.new(name);
+ forward_branch_instr_stream = riscv_rand_instr_stream::type_id::
+ create("forward_branch_instr_stream");
+ backward_branch_instr_stream = riscv_rand_instr_stream::type_id::
+ create("backward_branch_instr_stream");
+ jump_instr = riscv_instr_base::type_id::create("jump_instr");
+ endfunction
+
+ function void init(int instr_len);
+ branch_instr_stream_len = instr_len;
+ initialize_instr_list(branch_instr_offset-branch_instr_stream_len);
+ forward_branch_instr_stream.cfg = cfg;
+ backward_branch_instr_stream.cfg = cfg;
+ forward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
+ backward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
+ endfunction
+
+ virtual function void gen_instr(bit no_branch = 1'b0,
+ bit no_load_store = 1'b1,
+ bit enable_hint_instr = 1'b0);
+ int branch_offset;
+ super.gen_instr(1'b1);
+ forward_branch_instr_stream.gen_instr();
+ backward_branch_instr_stream.gen_instr();
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr, instr_name == JAL;)
+ jump_instr.imm_str = "test_done";
+ instr_list = {forward_branch_instr_stream.instr_list, instr_list,
+ jump_instr, backward_branch_instr_stream.instr_list};
+ foreach(instr_list[i]) begin
+ instr_list[i].atomic = 1'b1;
+ if(!instr_list[i].is_branch_target) begin
+ instr_list[i].has_label = 1'b0;
+ end
+ if(instr_list[i].category == BRANCH) begin
+ if(i < branch_instr_stream_len)
+ branch_offset = branch_instr_offset;
+ else
+ branch_offset = -branch_instr_offset;
+ instr_list[i].imm_str = $sformatf("target_%0d", i);
+ instr_list[i].branch_assigned = 1'b1;
+ // Avoid dead loop
+ if(((instr_list[i+branch_offset].category == BRANCH) ||
+ instr_list[i+branch_offset].is_branch_target) && (branch_offset < 0))
+ branch_offset = branch_offset + 1;
+ `uvm_info(get_full_name(), $sformatf("Branch [%0d] %0s -> [%0d] %0s", i,
+ instr_list[i].convert2asm(), i+branch_offset,
+ instr_list[i+branch_offset].convert2asm()), UVM_LOW)
+ if(i < -branch_offset)
+ `uvm_fatal(get_name(), $sformatf("Unexpected branch instr at %0d", i))
+ instr_list[i+branch_offset].label = $sformatf("target_%0d", i);
+ instr_list[i+branch_offset].has_label = 1'b1;
+ instr_list[i+branch_offset].is_branch_target = 1;
+ end
+ end
+ endfunction
+
+endclass
+
+class riscv_sw_interrupt_instr extends riscv_directed_instr_stream;
+
+ rand bit usip;
+ rand bit ssip;
+ rand bit msip;
+ rand privileged_reg_t ip_reg;
+ rand riscv_pseudo_instr li_instr;
+ rand riscv_instr_base csr_instr;
+ riscv_privil_reg ip;
+ rand riscv_reg_t rs1_reg;
+
+ constraint ip_reg_c {
+ if(cfg.init_privileged_mode == MACHINE_MODE) {
+ ip_reg == MIP;
+ } else {
+ ip_reg == SIP;
+ }
+ (ip_reg == MIP) -> (usip || ssip || msip);
+ (ip_reg == SIP) -> (usip || ssip);
+ }
+
+ constraint instr_c {
+ !(rs1_reg inside {cfg.reserved_regs});
+ rs1_reg != ZERO;
+ li_instr.pseudo_instr_name == LI;
+ li_instr.rd == rs1_reg;
+ csr_instr.instr_name == CSRRW;
+ csr_instr.rs1 == rs1_reg;
+ // TODO: Support non-zero rd for SIP, MIP
+ // csr_instr.rd inside {cfg.avail_regs};
+ csr_instr.rd == ZERO;
+ csr_instr.csr == ip_reg;
+ }
+
+ `uvm_object_utils(riscv_sw_interrupt_instr)
+
+ function new(string name = "");
+ super.new(name);
+ instr_list.rand_mode(0);
+ li_instr = riscv_pseudo_instr::type_id::create("li_instr");
+ csr_instr = riscv_instr_base::type_id::create("csr_instr");
+ ip = riscv_privil_reg::type_id::create("ip");
+ endfunction
+
+ function void post_randomize();
+ // TODO: Support UIP
+ if(cfg.init_privileged_mode == USER_MODE) return;
+ ip.init_reg(ip_reg);
+ if(ip_reg == SIP) begin
+ ip.set_field("USIP", usip);
+ ip.set_field("SSIP", ssip);
+ end else begin
+ ip.set_field("USIP", usip);
+ ip.set_field("SSIP", ssip);
+ ip.set_field("MSIP", msip);
+ end
+ li_instr.imm_str = $sformatf("0x%0x", ip.get_val());
+ csr_instr.comment = ip_reg.name();
+ instr_list = {li_instr, csr_instr};
+ super.post_randomize();
+ endfunction
+
+endclass
+
diff --git a/vendor/google_riscv-dv/src/riscv_illegal_instr.sv b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv
new file mode 100644
index 00000000..5ba3ca30
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv
@@ -0,0 +1,209 @@
+/*
+ * 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.
+ */
+
+// ---------------------------------------------------------------------------------------------
+// This class is used to generate illegal or HINT instructions.
+// The illegal instruction will be generated in binary format and mixed with other valid instr.
+// The mixed instruction stream will be stored in data section and loaded to instruction pages
+// at run time.
+// ---------------------------------------------------------------------------------------------
+
+class riscv_illegal_instr extends uvm_object;
+
+ typedef enum bit [2:0] {
+ kIllegalOpcode,
+ kIllegalFunc3,
+ kIllegalFunc7,
+ kReservedCompressedInstr,
+ kHintInstr
+ } illegal_instr_type_e;
+
+ bit [6:0] legal_opcode[$] = '{7'b0000011,
+ 7'b0000111,
+ 7'b0001111,
+ 7'b0010011,
+ 7'b0010111,
+ 7'b0011011,
+ 7'b0100011,
+ 7'b0100111,
+ 7'b0101111,
+ 7'b0110011,
+ 7'b0110111,
+ 7'b0111011,
+ 7'b1000011,
+ 7'b1000111,
+ 7'b1001011,
+ 7'b1001111,
+ 7'b1010011,
+ 7'b1100011,
+ 7'b1100111,
+ 7'b1101111,
+ 7'b1110011};
+
+ rand illegal_instr_type_e exception;
+ rand bit [31:0] instr_bin;
+ rand bit [6:0] opcode;
+ rand bit compressed;
+ rand bit [2:0] func3;
+ rand bit [6:0] func7;
+ rand bit has_func3;
+ rand bit has_func7;
+ rand bit [1:0] c_op;
+ rand bit [2:0] c_msb;
+ riscv_instr_gen_config cfg;
+
+ constraint instr_bit_assignment_c {
+ solve opcode before instr_bin;
+ solve func3 before instr_bin;
+ solve func7 before instr_bin;
+ if (compressed) {
+ instr_bin[1:0] == c_op;
+ instr_bin[15:13] == c_msb;
+ } else {
+ instr_bin[6:0] == opcode;
+ if (has_func7) {
+ instr_bin[31:25] == func7;
+ }
+ if (has_func3) {
+ instr_bin[14:12] == func3;
+ }
+ }
+ }
+
+ constraint exception_type_c {
+ if (compressed) {
+ exception inside {kReservedCompressedInstr, kHintInstr};
+ } else {
+ exception inside {kIllegalOpcode, kIllegalFunc3, kIllegalFunc7};
+ }
+ if (!has_func7) {
+ exception != kIllegalFunc7;
+ }
+ if (!has_func3) {
+ exception != kIllegalFunc3;
+ }
+ }
+
+ constraint compressed_instr_op_c {
+ c_op != 2'b11;
+ }
+
+ constraint reserved_compressed_instr_c {
+ if (exception == kReservedCompressedInstr) {
+ ((instr_bin[15:5] == '0) && (c_op == 2'b00)) ||
+ ((c_msb == 3'b100) && (c_op == 2'b00)) ||
+ ((instr_bin[15:10] == 6'b100111) && (instr_bin[6:5] == 2'b10) && (c_op == 2'b01)) ||
+ ((instr_bin[15:10] == 6'b100111) && (instr_bin[6:5] == 2'b11) && (c_op == 2'b01)) ||
+ ((c_msb == 3'b001) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b011) && (c_op == 2'b01) && (instr_bin[12:2] == 11'h40)) ||
+ ((c_msb == 3'b001) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b010) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b011) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b100) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0));
+ }
+ }
+
+ constraint hint_instr_c {
+ if (exception == kHintInstr) {
+ ((c_msb == 3'b000) && (c_op == 2'b01) && ({instr_bin[12], instr_bin[6:2]} == 6'b0)) ||
+ ((c_msb == 3'b010) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b011) && (c_op == 2'b01) && (instr_bin[11:7] == 5'b0)) ||
+ ((c_msb == 3'b100) && (c_op == 2'b10) && (instr_bin[11:7] == 5'b0) &&
+ (instr_bin[6:2] != 0));
+ }
+ }
+
+ constraint illegal_opcode_c {
+ if (exception == kIllegalOpcode) {
+ !(opcode inside {legal_opcode});
+ opcode[1:0] == 2'b11;
+ } else {
+ opcode inside {legal_opcode};
+ }
+ }
+
+ constraint illegal_func3_c {
+ solve opcode before func3;
+ if (!compressed) {
+ if (exception == kIllegalFunc3) {
+ (opcode == 7'b1100111) -> (func3 != 3'b000);
+ (opcode == 7'b1100011) -> (func3 inside {3'b010, 3'b011});
+ (opcode == 7'b0000011) -> (func3 == 3'b111);
+ (opcode == 7'b0100011) -> (func3 >= 3'b011);
+ (opcode == 7'b0001111) -> (!(func3 inside {3'b000, 3'b001}));
+ (opcode == 7'b1110011) -> (func3 == 3'b100);
+ (opcode == 7'b0011011) -> (!(func3 inside {3'b000, 3'b001, 3'b101}));
+ (opcode == 7'b0111011) -> (func3 inside {3'b010, 3'b011});
+ } else {
+ (opcode == 7'b1100111) -> (func3 == 3'b000);
+ (opcode == 7'b1100011) -> (!(func3 inside {3'b010, 3'b011}));
+ (opcode == 7'b0000011) -> (func3 != 3'b111);
+ (opcode == 7'b0100011) -> (func3 < 3'b011);
+ (opcode == 7'b0001111) -> (func3 inside {3'b000, 3'b001});
+ (opcode == 7'b1110011) -> (func3 != 3'b100);
+ (opcode == 7'b0011011) -> (func3 inside {3'b000, 3'b001, 3'b101});
+ (opcode == 7'b0111011) -> (!(func3 inside {3'b010, 3'b011}));
+ }
+ }
+ }
+
+ constraint has_func7_c {
+ solve opcode before func7;
+ if (((opcode == 7'b0010011) && (func3 inside {3'b001, 3'b101})) ||
+ (opcode inside {7'b0110011, 7'b0111011})) {
+ has_func7 == 1'b1;
+ } else {
+ has_func7 == 1'b0;
+ }
+ }
+
+ constraint has_func3_c {
+ solve opcode before func7;
+ if ((opcode inside {7'b0110111, 7'b1101111})) {
+ has_func3 == 1'b0;
+ } else {
+ has_func3 == 1'b1;
+ }
+ }
+
+ constraint illegal_func7_c {
+ if (!compressed) {
+ if (exception == kIllegalFunc7) {
+ !(func7 inside {7'b0, 7'b0100000, 7'b1});
+ } else {
+ func7 inside {7'b0, 7'b0100000, 7'b1};
+ }
+ }
+ }
+
+ // Avoid generating unsupported extensions - F, A, D
+ constraint unsupported_isa_opcode_c{
+ !(opcode inside {7'b0101111, 7'b0000111, 7'b0100111, 7'b1000111,
+ 7'b1001011, 7'b1001111, 7'b1010011, 7'b1000011});
+ }
+
+ `uvm_object_utils(riscv_illegal_instr)
+ `uvm_object_new
+
+ function string get_bin_str();
+ if (compressed) begin
+ get_bin_str = $sformatf("%4h", instr_bin[15:0]);
+ end else begin
+ get_bin_str = $sformatf("%8h", instr_bin[31:0]);
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_instr_base.sv b/vendor/google_riscv-dv/src/riscv_instr_base.sv
new file mode 100644
index 00000000..ecdaea9b
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_instr_base.sv
@@ -0,0 +1,887 @@
+/*
+ * Copyright 2018 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.
+ */
+
+class riscv_instr_base extends uvm_object;
+
+ rand riscv_instr_group_t group;
+ rand riscv_instr_format_t format;
+ rand bit [3:0] latency;
+ rand riscv_instr_cateogry_t category;
+ rand riscv_instr_name_t instr_name;
+ rand bit [11:0] csr;
+
+ rand riscv_reg_t rs2;
+ rand riscv_reg_t rs1;
+ rand riscv_reg_t rd;
+ rand bit [31:0] imm;
+ rand imm_t imm_type;
+ rand bit [4:0] imm_len;
+ rand bit is_pseudo_instr;
+ bit is_branch_target;
+ bit has_label = 1'b1;
+ bit atomic = 0;
+ bit branch_assigned;
+ bit process_load_store = 1'b1;
+ bit is_compressed;
+ bit is_illegal_instr;
+ bit is_hint_instr;
+
+ string imm_str;
+ string comment;
+ string label;
+ bit is_local_numeric_label;
+ int idx = -1;
+
+ `uvm_object_utils_begin(riscv_instr_base)
+ `uvm_field_enum(riscv_instr_group_t, group, UVM_DEFAULT)
+ `uvm_field_enum(riscv_instr_format_t, format, UVM_DEFAULT)
+ `uvm_field_enum(riscv_instr_cateogry_t, category, UVM_DEFAULT)
+ `uvm_field_enum(riscv_instr_name_t, instr_name, UVM_DEFAULT)
+ `uvm_field_enum(riscv_reg_t, rs2, UVM_DEFAULT)
+ `uvm_field_enum(riscv_reg_t, rs1, UVM_DEFAULT)
+ `uvm_field_enum(riscv_reg_t, rd, UVM_DEFAULT)
+ `uvm_field_int(imm, UVM_DEFAULT)
+ `uvm_field_enum(imm_t, imm_type, UVM_DEFAULT)
+ `uvm_object_utils_end
+
+ constraint default_c {
+ soft latency == 1;
+ soft is_pseudo_instr == 0;
+ instr_name != INVALID_INSTR;
+ }
+
+ // Immediate bit length for different instruction format
+ constraint imm_len_c {
+ solve imm_type before imm_len;
+ if(format inside {U_FORMAT, J_FORMAT}) {
+ imm_len == 20;
+ }
+ if(format inside {I_FORMAT, S_FORMAT, B_FORMAT}) {
+ if(imm_type == UIMM) {
+ imm_len == 5;
+ } else {
+ imm_len == 11;
+ }
+ }
+ if(format inside {CI_FORMAT, CSS_FORMAT}) {
+ // TODO: gcc compiler seems to not support 6 bits unsigned imm for c.lui,
+ // hardcoded to 5 bits for now
+ if(instr_name == C_LUI) {
+ imm_len == 5;
+ } else {
+ imm_len == 6;
+ }
+ }
+ if(format inside {CL_FORMAT, CS_FORMAT}) {
+ imm_len == 5;
+ }
+ if(format inside {CJ_FORMAT}) {
+ imm_len == 11;
+ }
+ if(format inside {CB_FORMAT, CIW_FORMAT}) {
+ if(instr_name == C_ANDI) {
+ imm_len == 6;
+ } else {
+ imm_len == 8;
+ }
+ }
+ }
+
+ constraint imm_val_c {
+ if(imm_type inside {NZIMM, NZUIMM}) {
+ imm != 0;
+ }
+ }
+
+ // Avoid generating HINT or illegal instruction by default as it's not supported by the compiler
+ constraint no_hint_illegal_instr_c {
+ if (instr_name inside {C_ADDI, C_ADDIW, C_LI, C_LUI, C_SLLI, C_SLLI64,
+ C_LQSP, C_LDSP, C_MV, C_ADD}) {
+ rd != ZERO;
+ }
+ if (instr_name == C_JR) {
+ rs1 != ZERO;
+ }
+ if (instr_name == C_MV) {
+ rs2 != ZERO;
+ }
+ }
+
+ // Registers specified by the three-bit rs1’, rs2’, and rd’ fields of the CIW, CL, CS,
+ // and CB formats
+ constraint compressed_three_bits_csr_c {
+ if(format inside {CIW_FORMAT, CL_FORMAT, CS_FORMAT, CB_FORMAT}) {
+ rs1 inside {[S0:A5]};
+ rs2 inside {[S0:A5]};
+ rd inside {[S0:A5]};
+ }
+ }
+
+ constraint fence_c {
+ if (instr_name == FENCE) {
+ rs1 == ZERO;
+ rd == ZERO;
+ imm == 0;
+ }
+ if (instr_name == FENCEI) {
+ rs1 == ZERO;
+ rd == ZERO;
+ imm == 0;
+ }
+ }
+
+ // Cannot shift more than the width of the bus
+ constraint shift_imm_val_c {
+ if(category == SHIFT) {
+ if(group == RV64I) {
+ // The new instruction in RV64I only handles 32 bits value
+ imm < 32;
+ } else {
+ imm < XLEN;
+ }
+ }
+ }
+
+ constraint load_store_c {
+ if(category inside {LOAD, STORE}) {
+ rs1 != ZERO; // x0 cannot be used to save the base address
+ }
+ }
+
+ constraint nop_c {
+ if(instr_name inside {NOP, C_NOP}) {
+ rs1 == ZERO;
+ rs2 == ZERO;
+ rd == ZERO;
+ }
+ }
+
+ constraint system_instr_c {
+ if (category inside {SYSTEM, SYNCH}) {
+ rd == ZERO;
+ rs1 == ZERO;
+ }
+ }
+
+ //////////// RV32I instructions //////////////
+ // LOAD instructions
+ `add_instr(LB, I_FORMAT, LOAD, RV32I)
+ `add_instr(LH, I_FORMAT, LOAD, RV32I)
+ `add_instr(LW, I_FORMAT, LOAD, RV32I)
+ `add_instr(LBU, I_FORMAT, LOAD, RV32I)
+ `add_instr(LHU, I_FORMAT, LOAD, RV32I)
+ // STORE instructions
+ `add_instr(SB, S_FORMAT, STORE, RV32I)
+ `add_instr(SH, S_FORMAT, STORE, RV32I)
+ `add_instr(SW, S_FORMAT, STORE, RV32I)
+ // SHIFT intructions
+ `add_instr(SLL, R_FORMAT, SHIFT, RV32I)
+ `add_instr(SLLI, I_FORMAT, SHIFT, RV32I)
+ `add_instr(SRL, R_FORMAT, SHIFT, RV32I)
+ `add_instr(SRLI, I_FORMAT, SHIFT, RV32I)
+ `add_instr(SRA, R_FORMAT, SHIFT, RV32I)
+ `add_instr(SRAI, I_FORMAT, SHIFT, RV32I)
+ // ARITHMETIC intructions
+ `add_instr(ADD, R_FORMAT, ARITHMETIC, RV32I)
+ `add_instr(ADDI, I_FORMAT, ARITHMETIC, RV32I)
+ `add_instr(NOP, I_FORMAT, ARITHMETIC, RV32I)
+ `add_instr(SUB, R_FORMAT, ARITHMETIC, RV32I)
+ `add_instr(LUI, U_FORMAT, ARITHMETIC, RV32I, UIMM)
+ `add_instr(AUIPC, U_FORMAT, ARITHMETIC, RV32I, UIMM)
+ // LOGICAL instructions
+ `add_instr(XOR, R_FORMAT, LOGICAL, RV32I)
+ `add_instr(XORI, I_FORMAT, LOGICAL, RV32I)
+ `add_instr(OR, R_FORMAT, LOGICAL, RV32I)
+ `add_instr(ORI, I_FORMAT, LOGICAL, RV32I)
+ `add_instr(AND, R_FORMAT, LOGICAL, RV32I)
+ `add_instr(ANDI, I_FORMAT, LOGICAL, RV32I)
+ // COMPARE instructions
+ `add_instr(SLT, R_FORMAT, COMPARE, RV32I)
+ `add_instr(SLTI, I_FORMAT, COMPARE, RV32I)
+ `add_instr(SLTU, R_FORMAT, COMPARE, RV32I)
+ `add_instr(SLTIU, I_FORMAT, COMPARE, RV32I)
+ // BRANCH instructions
+ `add_instr(BEQ, B_FORMAT, BRANCH, RV32I)
+ `add_instr(BNE, B_FORMAT, BRANCH, RV32I)
+ `add_instr(BLT, B_FORMAT, BRANCH, RV32I)
+ `add_instr(BGE, B_FORMAT, BRANCH, RV32I)
+ `add_instr(BLTU, B_FORMAT, BRANCH, RV32I)
+ `add_instr(BGEU, B_FORMAT, BRANCH, RV32I)
+ // JUMP instructions
+ `add_instr(JAL, J_FORMAT, JUMP, RV32I)
+ `add_instr(JALR, I_FORMAT, JUMP, RV32I)
+ // SYNCH instructions
+ `add_instr(FENCE, I_FORMAT, SYNCH, RV32I)
+ `add_instr(FENCEI, I_FORMAT, SYNCH, RV32I)
+ // SYSTEM instructions
+ `add_instr(ECALL, I_FORMAT, SYSTEM, RV32I)
+ `add_instr(EBREAK, I_FORMAT, SYSTEM, RV32I)
+ `add_instr(URET, I_FORMAT, SYSTEM, RV32I)
+ `add_instr(SRET, I_FORMAT, SYSTEM, RV32I)
+ `add_instr(MRET, I_FORMAT, SYSTEM, RV32I)
+ `add_instr(WFI, I_FORMAT, SYSTEM, RV32I)
+ // CSR instructions
+ `add_instr(CSRRW, R_FORMAT, CSR, RV32I, UIMM)
+ `add_instr(CSRRS, R_FORMAT, CSR, RV32I, UIMM)
+ `add_instr(CSRRC, R_FORMAT, CSR, RV32I, UIMM)
+ `add_instr(CSRRWI, I_FORMAT, CSR, RV32I, UIMM)
+ `add_instr(CSRRSI, I_FORMAT, CSR, RV32I, UIMM)
+ `add_instr(CSRRCI, I_FORMAT, CSR, RV32I, UIMM)
+
+ //////////// RV32M instructions //////////////
+ `add_instr(MUL, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(MULH, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(MULHSU, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(MULHU, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(DIV, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(DIVU, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(REM, R_FORMAT, ARITHMETIC, RV32M)
+ `add_instr(REMU, R_FORMAT, ARITHMETIC, RV32M)
+
+ //////////// RV64M instructions //////////////
+ `add_instr(MULW, R_FORMAT, ARITHMETIC, RV64M)
+ `add_instr(DIVW, R_FORMAT, ARITHMETIC, RV64M)
+ `add_instr(DIVUW, R_FORMAT, ARITHMETIC, RV64M)
+ `add_instr(REMW, R_FORMAT, ARITHMETIC, RV64M)
+ `add_instr(REMUW, R_FORMAT, ARITHMETIC, RV64M)
+
+ //////////// RV32F instructions //////////////
+ `add_instr(FLW, R_FORMAT, LOAD, RV32F)
+ `add_instr(FSW, R_FORMAT, STORE, RV32F)
+ `add_instr(FMADD_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMSUB_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FNMSUB_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FNMADD_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FADD_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FSUB_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMUL_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FDIV_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FSQRT_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FSGNJ_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FSGNJN_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FSGNJX_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMIN_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMAX_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_W_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_WU_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMV_X_W, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FEQ_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FLT_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FLE_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCLASS_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_S_W, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_S_WU, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FMV_W_X, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_L_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_LU_S, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_S_L, R_FORMAT, ARITHMETIC, RV32F)
+ `add_instr(FCVT_S_LU, R_FORMAT, ARITHMETIC, RV32F)
+
+ // RV64I instructions
+ // LOAD/STORE instructions
+ `add_instr(LWU, I_FORMAT, LOAD, RV64I)
+ `add_instr(LD, I_FORMAT, LOAD, RV64I)
+ `add_instr(SD, S_FORMAT, STORE, RV64I)
+ // SHIFT intructions
+ `add_instr(SLLW, R_FORMAT, SHIFT, RV64I)
+ `add_instr(SLLIW, I_FORMAT, SHIFT, RV64I)
+ `add_instr(SRLW, R_FORMAT, SHIFT, RV64I)
+ `add_instr(SRLIW, I_FORMAT, SHIFT, RV64I)
+ `add_instr(SRAW, R_FORMAT, SHIFT, RV64I)
+ `add_instr(SRAIW, I_FORMAT, SHIFT, RV64I)
+ // ARITHMETIC intructions
+ `add_instr(ADDW, R_FORMAT, ARITHMETIC, RV64I)
+ `add_instr(ADDIW, I_FORMAT, ARITHMETIC, RV64I)
+ `add_instr(SUBW, R_FORMAT, ARITHMETIC, RV64I)
+
+ // RV32IC
+ `add_instr(C_LW, CL_FORMAT, LOAD, RV32C, UIMM)
+ `add_instr(C_SW, CS_FORMAT, STORE, RV32C, UIMM)
+ `add_instr(C_LWSP, CI_FORMAT, LOAD, RV64C, UIMM)
+ `add_instr(C_SWSP, CSS_FORMAT, STORE, RV64C, UIMM)
+ `add_instr(C_ADDI4SPN, CIW_FORMAT, ARITHMETIC, RV32C, NZUIMM)
+ `add_instr(C_ADDI, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
+ `add_instr(C_ADDI16SP, CI_FORMAT, ARITHMETIC, RV32C, NZIMM)
+ `add_instr(C_LI, CI_FORMAT, ARITHMETIC, RV32C)
+ `add_instr(C_LUI, CI_FORMAT, ARITHMETIC, RV32C, NZUIMM)
+ `add_instr(C_SUB, CS_FORMAT, ARITHMETIC, RV32C)
+ `add_instr(C_ADD, CS_FORMAT, ARITHMETIC, RV32C)
+ `add_instr(C_NOP, CI_FORMAT, ARITHMETIC, RV32C)
+ `add_instr(C_MV, CR_FORMAT, ARITHMETIC, RV32C)
+ `add_instr(C_ANDI, CB_FORMAT, LOGICAL, RV32C)
+ `add_instr(C_XOR, CS_FORMAT, LOGICAL, RV32C)
+ `add_instr(C_OR, CS_FORMAT, LOGICAL, RV32C)
+ `add_instr(C_AND, CS_FORMAT, LOGICAL, RV32C)
+ `add_instr(C_BEQZ, CB_FORMAT, BRANCH, RV32C)
+ `add_instr(C_BNEZ, CB_FORMAT, BRANCH, RV32C)
+ `add_instr(C_SRLI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
+ `add_instr(C_SRAI, CB_FORMAT, SHIFT, RV32C, NZUIMM)
+ `add_instr(C_SLLI, CI_FORMAT, SHIFT, RV32C, NZUIMM)
+ `add_instr(C_J, CJ_FORMAT, JUMP, RV32C)
+ `add_instr(C_JAL, CJ_FORMAT, JUMP, RV32C)
+ `add_instr(C_JR, CR_FORMAT, JUMP, RV32C)
+ `add_instr(C_JALR, CR_FORMAT, JUMP, RV32C)
+ `add_instr(C_EBREAK, CI_FORMAT, SYSTEM, RV32C)
+
+ // RV64C
+ `add_instr(C_ADDIW, CI_FORMAT, ARITHMETIC, RV64C)
+ `add_instr(C_SUBW, CS_FORMAT, ARITHMETIC, RV64C)
+ `add_instr(C_ADDW, CS_FORMAT, ARITHMETIC, RV64C)
+ `add_instr(C_LD, CL_FORMAT, LOAD, RV64C, UIMM)
+ `add_instr(C_SD, CS_FORMAT, STORE, RV64C, UIMM)
+ `add_instr(C_LDSP, CI_FORMAT, LOAD, RV64C, UIMM)
+ `add_instr(C_SDSP, CSS_FORMAT, STORE, RV64C, UIMM)
+
+ // RV128C
+ `add_instr(C_SRLI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
+ `add_instr(C_SRAI64, CB_FORMAT, SHIFT, RV128C, NZUIMM)
+ `add_instr(C_SLLI64, CI_FORMAT, SHIFT, RV128C, NZUIMM)
+ `add_instr(C_LQ, CL_FORMAT, LOAD, RV32DC, UIMM)
+ `add_instr(C_SQ, CS_FORMAT, STORE, RV32DC, UIMM)
+ `add_instr(C_LQSP, CI_FORMAT, LOAD, RV32DC, UIMM)
+ `add_instr(C_SQSP, CSS_FORMAT, STORE, RV32DC, UIMM)
+
+ // RV32FC
+ `add_instr(C_FLW, CL_FORMAT, LOAD, RV32FC, UIMM)
+ `add_instr(C_FSW, CS_FORMAT, STORE, RV32FC, UIMM)
+ `add_instr(C_FLWSP, CI_FORMAT, LOAD, RV32FC, UIMM)
+ `add_instr(C_FSWSP, CSS_FORMAT, STORE, RV32FC, UIMM)
+
+ // RV32DC
+ `add_instr(C_FLD, CL_FORMAT, LOAD, RV32DC, UIMM)
+ `add_instr(C_FSD, CS_FORMAT, STORE, RV32DC, UIMM)
+ `add_instr(C_FLDSP, CI_FORMAT, LOAD, RV32DC, UIMM)
+ `add_instr(C_FSDSP, CSS_FORMAT, STORE, RV32DC, UIMM)
+
+ // Supervisor Instructions
+ `add_instr(SFENCE_VMA, R_FORMAT,SYNCH,RV32I)
+
+ function void post_randomize();
+ // Process the immediate value and sign extension
+ bit [31:0] imm_mask = '1;
+ imm_mask = imm_mask << imm_len;
+ if(imm_type inside {UIMM, NZUIMM}) begin
+ imm = imm & ~imm_mask;
+ end else begin
+ if(imm[imm_len-1])
+ imm = imm | imm_mask;
+ else
+ imm = imm & ~imm_mask;
+ end
+ // Give a random value if imm is zero after masking unexpectedly
+ if((imm_type inside {NZIMM, NZUIMM}) && (imm == '0)) begin
+ imm = $urandom_range(2 ** (imm_len-1) - 1, 1);
+ end
+ if (group inside {RV32C, RV64C, RV128C, RV32DC, RV32FC})
+ is_compressed = 1'b1;
+ if(imm_str == "")
+ imm_str = $sformatf("%0d", $signed(imm));
+ endfunction
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ // Convert the instrunction to one-liner print message
+ virtual function string convert2string();
+ return convert2asm();
+ endfunction
+
+ virtual function void do_print (uvm_printer printer);
+ `uvm_info(get_type_name(), convert2string(), UVM_LOW)
+ endfunction
+
+ // Convert the instruction to assembly code
+ virtual function string convert2asm(string prefix = "");
+ string asm_str;
+ asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
+ if(category != SYSTEM) begin
+ case(format)
+ J_FORMAT, U_FORMAT : // instr rd,imm
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
+ I_FORMAT: // instr rd,rs1,imm
+ if(instr_name == NOP)
+ asm_str = "nop";
+ else if(instr_name == FENCE)
+ asm_str = $sformatf("fence"); // TODO: Support all fence combinations
+ else if(instr_name == FENCEI)
+ asm_str = "fence.i";
+ else if(category == LOAD) // Use psuedo instruction format
+ asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name());
+ else if(category == CSR)
+ asm_str = $sformatf("%0s%0s, 0x%0x, %0s", asm_str, rd.name(), csr, get_imm());
+ else
+ asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), rs1.name(), get_imm());
+ S_FORMAT, B_FORMAT: // instr rs1,rs2,imm
+ if(category == STORE) // Use psuedo instruction format
+ asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name());
+ else
+ asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rs1.name(), rs2.name(), get_imm());
+ R_FORMAT: // instr rd,rs1,rs2
+ if(category == CSR)
+ asm_str = $sformatf("%0s%0s, 0x%0x, %0s", asm_str, rd.name(), csr, rs1.name());
+ else if(instr_name == SFENCE_VMA)
+ asm_str = "sfence.vma x0, x0"; // TODO: Support all possible sfence
+ else
+ asm_str = $sformatf("%0s%0s, %0s, %0s", asm_str, rd.name(), rs1.name(), rs2.name());
+ CI_FORMAT, CIW_FORMAT:
+ if(instr_name == C_NOP)
+ asm_str = "c.nop";
+ else
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
+ CL_FORMAT:
+ asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rd.name(), get_imm(), rs1.name());
+ CS_FORMAT:
+ if(category == STORE)
+ asm_str = $sformatf("%0s%0s, %0s(%0s)", asm_str, rs2.name(), get_imm(), rs1.name());
+ else
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rs1.name(), rs2.name());
+ CB_FORMAT:
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rs1.name(), get_imm());
+ CSS_FORMAT:
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rs2.name(), get_imm());
+ CR_FORMAT:
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), rs2.name());
+ CJ_FORMAT:
+ asm_str = $sformatf("%0s%0s", asm_str, get_imm());
+ endcase
+ end
+ if(comment != "")
+ asm_str = {asm_str, " #",comment};
+ return asm_str.tolower();
+ endfunction
+
+ function bit [6:0] get_opcode();
+ case (instr_name) inside
+ LUI : get_opcode = 7'b0110111;
+ AUIPC : get_opcode = 7'b0010111;
+ JAL : get_opcode = 7'b1101111;
+ JALR : get_opcode = 7'b1100111;
+ BEQ, BNE, BLT, BGE, BLTU, BGEU : get_opcode = 7'b1100011;
+ LB, LH, LW, LBU, LHU, LWU, LD : get_opcode = 7'b0000011;
+ SB, SH, SW, SD : get_opcode = 7'b0100011;
+ ADDI, SLTI, SLTIU, XORI, ORI, ANDI, SLLI, SRLI, SRAI, NOP : get_opcode = 7'b0010011;
+ ADD, SUB, SLL, SLT, SLTU, XOR, SRL, SRA, OR, AND, MUL,
+ MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU : get_opcode = 7'b0110011;
+ ADDIW, SLLIW, SRLIW, SRAIW : get_opcode = 7'b0011011;
+ MULH, MULHSU, MULHU, DIV, DIVU, REM, REMU : get_opcode = 7'b0110011;
+ FENCE, FENCEI : get_opcode = 7'b0001111;
+ ECALL, EBREAK, CSRRW, CSRRS, CSRRC, CSRRWI, CSRRSI, CSRRCI : get_opcode = 7'b1110011;
+ ADDW, SUBW, SLLW, SRLW, SRAW, MULW, DIVW, DIVUW, REMW, REMUW : get_opcode = 7'b0111011;
+ ECALL, EBREAK, URET, SRET, MRET, WFI, SFENCE_VMA : get_opcode = 7'b1110011;
+ default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
+ endcase
+ endfunction
+
+ // Get opcode for compressed instruction
+ function bit [1:0] get_c_opcode();
+ case (instr_name) inside
+ C_ADDI4SPN, C_FLD, C_FLD, C_LQ, C_LW, C_FLW,
+ C_LD, C_FSD, C_SQ, C_SW, C_FSW, C_SD : get_c_opcode = 2'b00;
+ C_NOP, C_ADDI, C_JAL, C_ADDIW, C_LI, C_ADDI16SP,
+ C_LUI, C_SRLI, C_SRLI64, C_SRAI, C_SRAI64,
+ C_ANDI, C_SUB, C_XOR, C_OR, C_AND, C_SUBW,
+ C_ADDW, C_J, C_BEQZ, C_BNEZ : get_c_opcode = 2'b01;
+ C_SLLI, C_SLLI64, C_FLDSP, C_LQSP, C_LWSP,
+ C_FLWSP, C_LDSP, C_JR, C_MV, C_EBREAK, C_JALR,
+ C_ADD, C_FSDSP, C_SQSP, C_SWSP, C_FSWSP, C_SDSP : get_c_opcode = 2'b10;
+ default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
+ endcase
+ endfunction
+
+ function bit [2:0] get_func3();
+ case (instr_name) inside
+ JALR : get_func3 = 3'b000;
+ BEQ : get_func3 = 3'b000;
+ BNE : get_func3 = 3'b001;
+ BLT : get_func3 = 3'b100;
+ BGE : get_func3 = 3'b101;
+ BLTU : get_func3 = 3'b110;
+ BGEU : get_func3 = 3'b111;
+ LB : get_func3 = 3'b000;
+ LH : get_func3 = 3'b001;
+ LW : get_func3 = 3'b010;
+ LBU : get_func3 = 3'b100;
+ LHU : get_func3 = 3'b101;
+ SB : get_func3 = 3'b000;
+ SH : get_func3 = 3'b001;
+ SW : get_func3 = 3'b010;
+ ADDI : get_func3 = 3'b000;
+ NOP : get_func3 = 3'b000;
+ SLTI : get_func3 = 3'b010;
+ SLTIU : get_func3 = 3'b011;
+ XORI : get_func3 = 3'b100;
+ ORI : get_func3 = 3'b110;
+ ANDI : get_func3 = 3'b111;
+ SLLI : get_func3 = 3'b001;
+ SRLI : get_func3 = 3'b101;
+ SRAI : get_func3 = 3'b101;
+ ADD : get_func3 = 3'b000;
+ SUB : get_func3 = 3'b000;
+ SLL : get_func3 = 3'b001;
+ SLT : get_func3 = 3'b010;
+ SLTU : get_func3 = 3'b011;
+ XOR : get_func3 = 3'b100;
+ SRL : get_func3 = 3'b101;
+ SRA : get_func3 = 3'b101;
+ OR : get_func3 = 3'b110;
+ AND : get_func3 = 3'b111;
+ FENCE : get_func3 = 3'b000;
+ FENCEI : get_func3 = 3'b001;
+ ECALL : get_func3 = 3'b000;
+ EBREAK : get_func3 = 3'b000;
+ CSRRW : get_func3 = 3'b001;
+ CSRRS : get_func3 = 3'b010;
+ CSRRC : get_func3 = 3'b011;
+ CSRRWI : get_func3 = 3'b101;
+ CSRRSI : get_func3 = 3'b110;
+ CSRRCI : get_func3 = 3'b111;
+ LWU : get_func3 = 3'b110;
+ LD : get_func3 = 3'b011;
+ SD : get_func3 = 3'b011;
+ ADDIW : get_func3 = 3'b000;
+ SLLIW : get_func3 = 3'b001;
+ SRLIW : get_func3 = 3'b101;
+ SRAIW : get_func3 = 3'b101;
+ ADDW : get_func3 = 3'b000;
+ SUBW : get_func3 = 3'b000;
+ SLLW : get_func3 = 3'b001;
+ SRLW : get_func3 = 3'b101;
+ SRAW : get_func3 = 3'b101;
+ MUL : get_func3 = 3'b000;
+ MULH : get_func3 = 3'b001;
+ MULHSU : get_func3 = 3'b010;
+ MULHU : get_func3 = 3'b011;
+ DIV : get_func3 = 3'b100;
+ DIVU : get_func3 = 3'b101;
+ REM : get_func3 = 3'b110;
+ REMU : get_func3 = 3'b111;
+ MULW : get_func3 = 3'b000;
+ DIVW : get_func3 = 3'b100;
+ DIVUW : get_func3 = 3'b101;
+ REMW : get_func3 = 3'b110;
+ REMUW : get_func3 = 3'b111;
+ C_ADDI4SPN : get_func3 = 3'b000;
+ C_FLD : get_func3 = 3'b001;
+ C_LQ : get_func3 = 3'b001;
+ C_LW : get_func3 = 3'b010;
+ C_FLW : get_func3 = 3'b011;
+ C_LD : get_func3 = 3'b011;
+ C_FSD : get_func3 = 3'b101;
+ C_SQ : get_func3 = 3'b101;
+ C_SW : get_func3 = 3'b110;
+ C_FSW : get_func3 = 3'b111;
+ C_SD : get_func3 = 3'b111;
+ C_NOP : get_func3 = 3'b000;
+ C_ADDI : get_func3 = 3'b000;
+ C_JAL : get_func3 = 3'b001;
+ C_ADDIW : get_func3 = 3'b001;
+ C_LI : get_func3 = 3'b010;
+ C_ADDI16SP : get_func3 = 3'b011;
+ C_LUI : get_func3 = 3'b011;
+ C_SRLI : get_func3 = 3'b100;
+ C_SRLI64 : get_func3 = 3'b100;
+ C_SRAI : get_func3 = 3'b100;
+ C_SRAI64 : get_func3 = 3'b100;
+ C_ANDI : get_func3 = 3'b100;
+ C_SUB : get_func3 = 3'b100;
+ C_XOR : get_func3 = 3'b100;
+ C_OR : get_func3 = 3'b100;
+ C_AND : get_func3 = 3'b100;
+ C_SUBW : get_func3 = 3'b100;
+ C_ADDW : get_func3 = 3'b100;
+ C_J : get_func3 = 3'b101;
+ C_BEQZ : get_func3 = 3'b110;
+ C_BNEZ : get_func3 = 3'b111;
+ C_SLLI : get_func3 = 3'b000;
+ C_SLLI64 : get_func3 = 3'b000;
+ C_FLDSP : get_func3 = 3'b001;
+ C_LQSP : get_func3 = 3'b001;
+ C_LWSP : get_func3 = 3'b010;
+ C_FLWSP : get_func3 = 3'b011;
+ C_LDSP : get_func3 = 3'b011;
+ C_JR : get_func3 = 3'b100;
+ C_MV : get_func3 = 3'b100;
+ C_EBREAK : get_func3 = 3'b100;
+ C_JALR : get_func3 = 3'b100;
+ C_ADD : get_func3 = 3'b100;
+ C_FSDSP : get_func3 = 3'b101;
+ C_SQSP : get_func3 = 3'b101;
+ C_SWSP : get_func3 = 3'b110;
+ C_FSWSP : get_func3 = 3'b111;
+ C_SDSP : get_func3 = 3'b111;
+ ECALL, EBREAK, URET, SRET, MRET, WFI, SFENCE_VMA : get_func3 = 3'b000;
+ default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
+ endcase
+ endfunction
+
+ function bit [6:0] get_func7();
+ case (instr_name)
+ SLLI : get_func7 = 7'b0000000;
+ SRLI : get_func7 = 7'b0000000;
+ SRAI : get_func7 = 7'b0100000;
+ ADD : get_func7 = 7'b0000000;
+ SUB : get_func7 = 7'b0100000;
+ SLL : get_func7 = 7'b0000000;
+ SLT : get_func7 = 7'b0000000;
+ SLTU : get_func7 = 7'b0000000;
+ XOR : get_func7 = 7'b0000000;
+ SRL : get_func7 = 7'b0000000;
+ SRA : get_func7 = 7'b0100000;
+ OR : get_func7 = 7'b0000000;
+ AND : get_func7 = 7'b0000000;
+ FENCE : get_func7 = 7'b0000000;
+ FENCEI : get_func7 = 7'b0000000;
+ ECALL : get_func7 = 7'b0000000;
+ EBREAK : get_func7 = 7'b0000000;
+ SLLIW : get_func7 = 7'b0000000;
+ SRLIW : get_func7 = 7'b0000000;
+ SRAIW : get_func7 = 7'b0100000;
+ ADDW : get_func7 = 7'b0000000;
+ SUBW : get_func7 = 7'b0100000;
+ SLLW : get_func7 = 7'b0000000;
+ SRLW : get_func7 = 7'b0000000;
+ SRAW : get_func7 = 7'b0100000;
+ MUL : get_func7 = 7'b0000001;
+ MULH : get_func7 = 7'b0000001;
+ MULHSU : get_func7 = 7'b0000001;
+ MULHU : get_func7 = 7'b0000001;
+ DIV : get_func7 = 7'b0000001;
+ DIVU : get_func7 = 7'b0000001;
+ REM : get_func7 = 7'b0000001;
+ REMU : get_func7 = 7'b0000001;
+ MULW : get_func7 = 7'b0000001;
+ DIVW : get_func7 = 7'b0000001;
+ DIVUW : get_func7 = 7'b0000001;
+ REMW : get_func7 = 7'b0000001;
+ REMUW : get_func7 = 7'b0000001;
+ ECALL : get_func7 = 7'b0000000;
+ EBREAK : get_func7 = 7'b0000000;
+ URET : get_func7 = 7'b0000000;
+ SRET : get_func7 = 7'b0001000;
+ MRET : get_func7 = 7'b0011000;
+ WFI : get_func7 = 7'b0001000;
+ SFENCE_VMA: get_func7 = 7'b0001001;
+ default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
+ endcase
+ endfunction
+
+ // Convert the instruction to assembly code
+ virtual function string convert2bin(string prefix = "");
+ string binary;
+ if (!is_compressed) begin
+ case(format)
+ J_FORMAT: begin
+ binary = $sformatf("%8h", {imm[20], imm[10:1], imm[11], imm[19:12], rd, get_opcode()});
+ end
+ U_FORMAT: begin
+ binary = $sformatf("%8h", {imm[31:12], rd, get_opcode()});
+ end
+ I_FORMAT: begin
+ if(instr_name inside {FENCE, FENCEI})
+ binary = $sformatf("%8h", {17'b0, get_func3(), 5'b0, get_opcode()});
+ else if(category == CSR)
+ binary = $sformatf("%8h", {csr[10:0], imm[4:0], get_func3(), rd, get_opcode()});
+ else if(instr_name == ECALL)
+ binary = $sformatf("%8h", {get_func7(), 18'b0, get_opcode()});
+ else if(instr_name inside {URET, SRET, MRET})
+ binary = $sformatf("%8h", {get_func7(), 5'b10, 13'b0, get_opcode()});
+ else if(instr_name == EBREAK)
+ binary = $sformatf("%8h", {get_func7(), 5'b01, 13'b0, get_opcode()});
+ else if(instr_name == WFI)
+ binary = $sformatf("%8h", {get_func7(), 5'b101, 13'b0, get_opcode()});
+ else
+ binary = $sformatf("%8h", {imm[11:0], rs1, get_func3(), rd, get_opcode()});
+ end
+ S_FORMAT: begin
+ binary = $sformatf("%8h", {imm[11:5], rs2, rs1, get_func3(), imm[4:0], get_opcode()});
+ end
+ B_FORMAT: begin
+ binary = $sformatf("%8h",
+ {imm[12], imm[10:5], rs2, rs1, get_func3(),
+ imm[4:1], imm[11], get_opcode()});
+ end
+ R_FORMAT: begin
+ if(category == CSR)
+ binary = $sformatf("%8h", {csr[10:0], rs1, get_func3(), rd, get_opcode()});
+ else if(instr_name == SFENCE_VMA)
+ binary = $sformatf("%8h", {get_func7(), 18'b0, get_opcode()});
+ else
+ binary = $sformatf("%8h", {get_func7(), rs2, rs1, get_func3(), rd, get_opcode()});
+ end
+ endcase
+ end else begin
+ case (instr_name) inside
+ C_ADDI4SPN:
+ binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[9:6],
+ imm[2], imm[3], get_c_gpr(rd), get_c_opcode()});
+ C_LQ:
+ binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[8],
+ get_c_gpr(rs1), imm[7:6], get_c_gpr(rd), get_c_opcode()});
+ C_FLD, C_LD:
+ binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
+ imm[7:6], get_c_gpr(rd), get_c_opcode()});
+ C_LW, C_FLW:
+ binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
+ imm[2], imm[6], get_c_gpr(rd), get_c_opcode()});
+ C_SQ:
+ binary = $sformatf("%4h", {get_func3(), imm[5:4], imm[8],
+ get_c_gpr(rs1), imm[7:6], get_c_gpr(rs2), get_c_opcode()});
+ C_FSD, C_SD:
+ binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
+ imm[7:6], get_c_gpr(rs2), get_c_opcode()});
+ C_SW, C_FSW:
+ binary = $sformatf("%4h", {get_func3(), imm[5:3], get_c_gpr(rs1),
+ imm[2], imm[6], get_c_gpr(rs2), get_c_opcode()});
+ C_NOP, C_ADDI, C_LI, C_ADDIW:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
+ C_JAL, C_J:
+ binary = $sformatf("%4h", {get_func3(), imm[11], imm[4], imm[9:8],
+ imm[10], imm[6], imm[7], imm[3:1], imm[5], get_c_opcode()});
+ C_ADDI16SP:
+ binary = $sformatf("%4h", {get_func3(), imm[9], 5'b10,
+ imm[4], imm[6], imm[8:7], imm[5], get_c_opcode()});
+ C_LUI:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
+ C_SRLI:
+ binary = $sformatf("%4h", {get_func3(), imm[5],
+ 2'b0, get_c_gpr(rd), imm[4:0], get_c_opcode()});
+ C_SRLI64:
+ binary = $sformatf("%4h", {get_func3(), 3'b0, get_c_gpr(rd), 5'b0, get_c_opcode()});
+ C_SRAI:
+ binary = $sformatf("%4h", {get_func3(), imm[5],
+ 2'b01, get_c_gpr(rd), imm[4:0], get_c_opcode()});
+ C_SRAI64:
+ binary = $sformatf("%4h", {get_func3(), 3'b001,
+ get_c_gpr(rd), 5'b0, get_c_opcode()});
+ C_ANDI:
+ binary = $sformatf("%4h", {get_func3(), imm[5],
+ 2'b10, get_c_gpr(rd), imm[4:0], get_c_opcode()});
+ C_SUB:
+ binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
+ 2'b00, get_c_gpr(rs2), get_c_opcode()});
+ C_XOR:
+ binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
+ 2'b01, get_c_gpr(rs2), get_c_opcode()});
+ C_OR:
+ binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
+ 2'b10, get_c_gpr(rs2), get_c_opcode()});
+ C_AND:
+ binary = $sformatf("%4h", {get_func3(), 3'b011, get_c_gpr(rd),
+ 2'b11, get_c_gpr(rs2), get_c_opcode()});
+ C_SUBW:
+ binary = $sformatf("%4h", {get_func3(), 3'b111, get_c_gpr(rd),
+ 2'b00, get_c_gpr(rs2), get_c_opcode()});
+ C_ADDW:
+ binary = $sformatf("%4h", {get_func3(), 3'b111, get_c_gpr(rd),
+ 2'b01, get_c_gpr(rs2), get_c_opcode()});
+ C_BEQZ, C_BNEZ:
+ binary = $sformatf("%4h", {get_func3(), imm[8], imm[4:3],
+ get_c_gpr(rs1), imm[7:6], imm[2:1], imm[5], get_c_opcode()});
+ C_SLLI:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:0], get_c_opcode()});
+ C_SLLI64:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, rd, 5'b0, get_c_opcode()});
+ C_FLDSP, C_LDSP:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:3], imm[8:6], get_c_opcode()});
+ C_LQSP:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4], imm[9:6], get_c_opcode()});
+ C_LWSP, C_FLWSP:
+ binary = $sformatf("%4h", {get_func3(), imm[5], rd, imm[4:2], imm[7:6], get_c_opcode()});
+ C_JR:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, rs1, 5'b0, get_c_opcode()});
+ C_MV:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, rd, rs2, get_c_opcode()});
+ C_EBREAK:
+ binary = $sformatf("%4h", {get_func3(), 1'b1, 10'b0, get_c_opcode()});
+ C_JALR:
+ binary = $sformatf("%4h", {get_func3(), 1'b1, 10'b0, get_c_opcode()});
+ C_ADD:
+ binary = $sformatf("%4h", {get_func3(), 1'b1, rd, rs2, get_c_opcode()});
+ C_FSDSP, C_SDSP:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:3], imm[8:6], rs2, get_c_opcode()});
+ C_SQSP:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:4], imm[9:6], rs2, get_c_opcode()});
+ C_SWSP, C_FSWSP:
+ binary = $sformatf("%4h", {get_func3(), 1'b0, imm[5:2], imm[7:6], rs2, get_c_opcode()});
+ default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
+ endcase
+ end
+ return {prefix, binary};
+ endfunction
+
+ virtual function string get_instr_name();
+ get_instr_name = instr_name.name();
+ if(get_instr_name.substr(0, 1) == "C_") begin
+ get_instr_name = {"c.", get_instr_name.substr(2, get_instr_name.len() - 1)};
+ end
+ return get_instr_name;
+ endfunction
+
+ // Get RVC register name for CIW, CL, CS, CB format
+ function bit [2:0] get_c_gpr(riscv_reg_t gpr);
+ return gpr[2:0];
+ endfunction
+
+ // Default return imm value directly, can be overriden to use labels and symbols
+ // Example: %hi(symbol), %pc_rel(label) ...
+ virtual function string get_imm();
+ return imm_str;
+ endfunction
+
+ virtual function void clear_unused_label();
+ if(has_label && !is_branch_target && is_local_numeric_label) begin
+ has_label = 1'b0;
+ end
+ endfunction
+
+endclass
+
+// Psuedo instructions are used to simplify assembly program writing
+class riscv_pseudo_instr extends riscv_instr_base;
+
+ rand riscv_pseudo_instr_name_t pseudo_instr_name;
+
+ constraint default_c {
+ is_pseudo_instr == 1'b1;
+ }
+
+ `add_pseudo_instr(LI, I_FORMAT, LOAD, RV32I)
+ `add_pseudo_instr(LA, I_FORMAT, LOAD, RV32I)
+
+ `uvm_object_utils(riscv_pseudo_instr)
+
+ function new(string name = "");
+ super.new(name);
+ process_load_store = 0;
+ endfunction
+
+ // Convert the instruction to assembly code
+ virtual function string convert2asm(string prefix = "");
+ string asm_str;
+ asm_str = format_string(get_instr_name(), MAX_INSTR_STR_LEN);
+ // instr rd,imm
+ asm_str = $sformatf("%0s%0s, %0s", asm_str, rd.name(), get_imm());
+ if(comment != "")
+ asm_str = {asm_str, " #",comment};
+ return asm_str.tolower();
+ endfunction
+
+ virtual function string get_instr_name();
+ return pseudo_instr_name.name();
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv
new file mode 100644
index 00000000..5eb6a348
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_instr_gen_config.sv
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//-----------------------------------------------------------------------------
+// RISC-V assembly program generator configuration class
+//-----------------------------------------------------------------------------
+
+class riscv_instr_gen_config extends uvm_object;
+
+ //-----------------------------------------------------------------------------
+ // Random instruction generation settings
+ //-----------------------------------------------------------------------------
+
+ // Instruction count of the main program
+ rand int main_program_instr_cnt;
+
+ // Instruction count of each sub-program
+ rand int sub_program_instr_cnt[];
+
+ // Pattern of data section: RAND_DATA, ALL_ZERO, INCR_VAL
+ rand data_pattern_t data_page_pattern;
+
+ // Max depth for the nested loops
+ rand bit [1:0] max_nested_loop;
+
+ // Associate array for delegation configuration for each exception and interrupt
+ // When the bit is 1, the corresponding delegation is enabled.
+ rand bit m_mode_exception_delegation[exception_cause_t];
+ rand bit s_mode_exception_delegation[exception_cause_t];
+ rand bit m_mode_interrupt_delegation[interrupt_cause_t];
+ rand bit s_mode_interrupt_delegation[interrupt_cause_t];
+
+ // Priviledged mode after boot
+ rand privileged_mode_t init_privileged_mode;
+
+ // Key fields in xSTATUS
+ // Memory protection bits
+ rand bit mstatus_mprv;
+ rand bit mstatus_mxr;
+ rand bit mstatus_sum;
+ rand bit mstatus_tvm;
+
+ // Enable sfence.vma instruction
+ rand bit enable_sfence;
+
+ //-----------------------------------------------------------------------------
+ // Command line options or control knobs
+ //-----------------------------------------------------------------------------
+ // Main options for RISC-V assembly program generation
+ // Number of sub-programs per test
+ int num_of_sub_program = 5;
+ int instr_cnt = 200;
+ int num_of_tests = 1;
+ // For tests doesn't involve load/store, the data section generation could be skipped
+ bit no_data_page;
+ // Options to turn off some specific types of instructions
+ bit no_branch_jump; // No branch/jump instruction
+ bit no_load_store; // No load/store instruction
+ bit no_csr_instr = 1; // No csr instruction
+ bit no_ebreak = 1; // No ebreak instruction
+ bit no_fence; // No fence instruction
+ bit enable_illegal_instruction;
+ bit enable_hint_instruction;
+ int bin_program_instr_cnt = 200;
+ // Directed boot privileged mode, u, m, s
+ string boot_mode_opts;
+ int enable_page_table_exception;
+ bit no_directed_instr;
+ // A name suffix for the generated assembly program
+ string asm_test_suffix;
+ // Enable interrupt bit in MSTATUS (MIE, SIE, UIE)
+ int enable_interrupt;
+ // sfence support
+ bit allow_sfence_exception = 0;
+ // Interrupt/Exception Delegation
+ bit no_delegation = 1;
+ bit force_m_delegation = 0;
+ bit force_s_delegation = 0;
+ bit support_supervisor_mode;
+ // Stack space allocated to each program, need to be enough to store necessary context
+ // Example: RA, SP, T0, loop registers
+ int min_stack_len_per_program = 10 * (XLEN/8);
+ int max_stack_len_per_program = 16 * (XLEN/8);
+ // Maximum branch distance, avoid skipping large portion of the code
+ int max_branch_step = 20;
+ // Reserved registers
+ // Default reserved registers, only used by special instructions
+ riscv_reg_t default_reserved_regs[];
+ // Reserve some registers for loop counter, make sure the loop can execute
+ // in a determinstic way and not affected by other random instructions
+ rand riscv_reg_t loop_regs[];
+ // All reserved regs
+ riscv_reg_t reserved_regs[];
+
+ uvm_cmdline_processor inst;
+
+ constraint default_c {
+ sub_program_instr_cnt.size() == num_of_sub_program;
+ main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
+ main_program_instr_cnt inside {[1 : instr_cnt]};
+ foreach(sub_program_instr_cnt[i]) {
+ sub_program_instr_cnt[i] inside {[1 : instr_cnt]};
+ }
+ // Disable sfence if the program is not boot to supervisor mode
+ // If sfence exception is allowed, we can enable sfence instruction in any priviledged mode.
+ // When MSTATUS.TVM is set, executing sfence.vma will be treate as illegal instruction
+ if(allow_sfence_exception) {
+ enable_sfence == 1'b1;
+ (init_privileged_mode != SUPERVISOR_MODE) || (mstatus_tvm == 1'b1);
+ } else {
+ (init_privileged_mode != SUPERVISOR_MODE || !riscv_instr_pkg::support_sfence || mstatus_tvm || no_fence)
+ -> (enable_sfence == 1'b0);
+ }
+ }
+
+ // Boot privileged mode distribution
+ constraint boot_privileged_mode_dist_c {
+ // Boot to higher privileged mode more often
+ if(riscv_instr_pkg::supported_privileged_mode.size() == 2) {
+ init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 9,
+ riscv_instr_pkg::supported_privileged_mode[1] := 1};
+ } else if (riscv_instr_pkg::supported_privileged_mode.size() == 3) {
+ init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 6,
+ riscv_instr_pkg::supported_privileged_mode[1] := 3,
+ riscv_instr_pkg::supported_privileged_mode[2] := 1};
+ } else {
+ init_privileged_mode == riscv_instr_pkg::supported_privileged_mode[0];
+ }
+ }
+
+ constraint mstatus_c {
+ // This is default disabled at setup phase. It can be enabled in the exception and interrupt
+ // handling routine
+ mstatus_mprv == 1'b0;
+ }
+
+ // Exception delegation setting
+ constraint exception_delegation_c {
+ // Do not delegate instructino page fault to supervisor/user mode because this may introduce
+ // dead loop. All the subsequent instruction fetches may fail and program cannot recover.
+ m_mode_exception_delegation[INSTRUCTION_PAGE_FAULT] == 1'b0;
+ if(force_m_delegation) {
+ foreach(m_mode_exception_delegation[i]) {
+ soft m_mode_exception_delegation[i] == 1'b1;
+ }
+ foreach(m_mode_interrupt_delegation[i]) {
+ soft m_mode_interrupt_delegation[i] == 1'b1;
+ }
+ }
+ if(force_s_delegation) {
+ foreach(s_mode_exception_delegation[i]) {
+ soft s_mode_exception_delegation[i] == 1'b1;
+ }
+ foreach(s_mode_interrupt_delegation[i]) {
+ soft s_mode_interrupt_delegation[i] == 1'b1;
+ }
+ }
+ }
+
+ // Spike only supports a subset of exception and interrupt delegation
+ // You can modify this constraint if your ISS support different set of delegations
+ constraint delegation_c {
+ foreach(m_mode_exception_delegation[i]) {
+ if(!support_supervisor_mode) {
+ m_mode_exception_delegation[i] == 0;
+ }
+ if(!(i inside {INSTRUCTION_ADDRESS_MISALIGNED, BREAKPOINT, ECALL_UMODE,
+ INSTRUCTION_PAGE_FAULT, LOAD_PAGE_FAULT, STORE_AMO_PAGE_FAULT})) {
+ m_mode_exception_delegation[i] == 0;
+ }
+ }
+ foreach(m_mode_interrupt_delegation[i]) {
+ if(!support_supervisor_mode) {
+ m_mode_interrupt_delegation[i] == 0;
+ }
+ if(!(i inside {S_SOFTWARE_INTR, S_TIMER_INTR, S_EXTERNAL_INTR})) {
+ m_mode_interrupt_delegation[i] == 0;
+ }
+ }
+ }
+
+ constraint reserved_reg_c {
+ unique {loop_regs};
+ foreach(loop_regs[i]) {
+ !(loop_regs[i] inside {default_reserved_regs});
+ }
+ }
+
+ constraint legal_loop_regs_c {
+ soft max_nested_loop != 0;
+ // One register for loop counter, one for loop limit
+ loop_regs.size() == max_nested_loop*2;
+ unique {loop_regs};
+ foreach(loop_regs[i]) {
+ loop_regs[i] != ZERO;
+ }
+ }
+
+ `uvm_object_utils_begin(riscv_instr_gen_config)
+ `uvm_field_int(main_program_instr_cnt, UVM_DEFAULT)
+ `uvm_field_sarray_int(sub_program_instr_cnt, UVM_DEFAULT)
+ `uvm_object_utils_end
+
+ 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);
+ get_int_arg_value("+enable_page_table_exception=", enable_page_table_exception);
+ get_int_arg_value("+enable_interrupt=", enable_interrupt);
+ get_int_arg_value("+num_of_sub_program=", num_of_sub_program);
+ get_int_arg_value("+instr_cnt=", instr_cnt);
+ get_bool_arg_value("+no_ebreak=", no_ebreak);
+ get_bool_arg_value("+no_branch_jump=", no_branch_jump);
+ get_bool_arg_value("+no_load_store=", no_load_store);
+ get_bool_arg_value("+no_csr_instr=", no_csr_instr);
+ get_bool_arg_value("+allow_sfence_exception=", allow_sfence_exception);
+ get_bool_arg_value("+no_data_page=", no_data_page);
+ get_bool_arg_value("+no_directed_instr=", no_directed_instr);
+ get_bool_arg_value("+no_fence=", no_fence);
+ get_bool_arg_value("+no_delegation=", no_delegation);
+ get_bool_arg_value("+enable_illegal_instruction=", enable_illegal_instruction);
+ get_bool_arg_value("+enable_hint_instruction=", enable_hint_instruction);
+ get_bool_arg_value("+force_m_delegation=", force_m_delegation);
+ get_bool_arg_value("+force_s_delegation=", force_s_delegation);
+ 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)
+ case(boot_mode_opts)
+ "m" : init_privileged_mode = MACHINE_MODE;
+ "s" : init_privileged_mode = SUPERVISOR_MODE;
+ "u" : init_privileged_mode = USER_MODE;
+ default: `uvm_fatal(get_full_name(),
+ $sformatf("Illegal boot mode option - %0s", boot_mode_opts))
+ endcase
+ init_privileged_mode.rand_mode(0);
+ end
+ `uvm_info(`gfn, $sformatf("riscv_instr_pkg::supported_privileged_mode = %0d",
+ riscv_instr_pkg::supported_privileged_mode.size()), UVM_LOW)
+ void'(inst.get_arg_value("+asm_test_suffix=", asm_test_suffix));
+ // Directed march list from the runtime options, ex. RV32I, RV32M etc.
+ void'(inst.get_arg_value("+march=", s));
+ if(s != "") begin
+ string cmdline_march_list[$];
+ riscv_instr_group_t march;
+ uvm_split_string(s, ",", cmdline_march_list);
+ riscv_instr_pkg::supported_isa.delete();
+ foreach(cmdline_march_list[i]) begin
+ if(uvm_enum_wrapper#(riscv_instr_group_t)::from_name(cmdline_march_list[i], march)) begin
+ riscv_instr_pkg::supported_isa.push_back(march);
+ end else begin
+ `uvm_fatal(get_full_name(), $sformatf(
+ "Invalid march %0s specified in command line", cmdline_march_list[i]))
+ end
+ end
+ end
+ endfunction
+
+ // Initialize the exception/interrupt delegation associate array, set all delegation default to 0
+ virtual function void init_delegation();
+ exception_cause_t cause;
+ interrupt_cause_t intr_cause;
+ cause = cause.first;
+ // Init exception delegation array
+ do begin
+ m_mode_exception_delegation[cause] = 1'b0;
+ s_mode_exception_delegation[cause] = 1'b0;
+ cause = cause.next;
+ end
+ while(cause != cause.first);
+ // Init interrupt delegation array
+ intr_cause = intr_cause.first;
+ do begin
+ m_mode_interrupt_delegation[intr_cause] = 1'b0;
+ s_mode_interrupt_delegation[intr_cause] = 1'b0;
+ intr_cause = intr_cause.next;
+ end
+ 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 = {RA, // x1, return address
+ SP, // x2, stack pointer (user stack)
+ TP, // x4, thread pointer, used as kernel stack pointer
+ T0 // x5, alternative link 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)
+ support_supervisor_mode = 1;
+ end
+ endfunction
+
+ function void post_randomize();
+ // Setup the list all reserved registers
+ reserved_regs = {default_reserved_regs, loop_regs};
+ // Need to save all loop registers, and RA/T0
+ min_stack_len_per_program = (max_nested_loop * 2 + 2) * (XLEN/8);
+ // Check if the setting is legal
+ check_setting();
+ endfunction
+
+ function void check_setting();
+ bit support_64b;
+ bit support_128b;
+ foreach (riscv_instr_pkg::supported_isa[i]) begin
+ if (riscv_instr_pkg::supported_isa[i] inside {RV64I, RV64M, RV64A, RV64F, RV64D, RV64C}) begin
+ support_64b = 1'b1;
+ end else if (riscv_instr_pkg::supported_isa[i] inside {RV128I, RV128C}) begin
+ support_128b = 1'b1;
+ end
+ end
+ if (support_128b && XLEN != 128) begin
+ `uvm_fatal(`gfn, "XLEN should be set to 128 based on riscv_instr_pkg::supported_isa setting")
+ end
+ if (!support_128b && support_64b && XLEN != 64) begin
+ `uvm_fatal(`gfn, "XLEN should be set to 64 based on riscv_instr_pkg::supported_isa setting")
+ end
+ if (!(support_128b || support_64b) && XLEN != 32) begin
+ `uvm_fatal(`gfn, "XLEN should be set to 32 based on riscv_instr_pkg::supported_isa setting")
+ end
+ if (!(support_128b || support_64b) && !(SATP_MODE inside {SV32, BARE})) begin
+ `uvm_fatal(`gfn, $sformatf("SATP mode %0s is not supported for RV32G ISA", SATP_MODE.name()))
+ end
+ endfunction
+
+ // Get an integer argument from comand line
+ function void get_int_arg_value(string cmdline_str, ref int val);
+ string s;
+ if(inst.get_arg_value(cmdline_str, s))
+ val = s.atoi();
+ endfunction
+
+ // Get a bool argument from comand line
+ function void get_bool_arg_value(string cmdline_str, ref bit val);
+ string s;
+ if(inst.get_arg_value(cmdline_str, s))
+ val = s.atobin();
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv
new file mode 100644
index 00000000..fb039842
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv
@@ -0,0 +1,753 @@
+/*
+ * Copyright 2018 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.
+ */
+
+package riscv_instr_pkg;
+
+ import uvm_pkg::*;
+
+ `include "uvm_macros.svh"
+ `include "dv_defines.svh"
+ `include "riscv_defines.svh"
+
+ typedef enum bit [3:0] {
+ BARE = 4'b0000,
+ SV32 = 4'b0001,
+ SV39 = 4'b1000,
+ SV48 = 4'b1001,
+ SV57 = 4'b1010,
+ SV64 = 4'b1011
+ } satp_mode_t;
+
+ typedef enum bit [2:0] {
+ IMM, // Signed immediate
+ UIMM, // Unsigned immediate
+ NZUIMM, // Non-zero unsigned immediate
+ NZIMM // Non-zero signed immediate
+ } imm_t;
+
+ // Privileged mode
+ typedef enum bit [1:0] {
+ USER_MODE = 2'b00,
+ SUPERVISOR_MODE = 2'b01,
+ RESERVED_MODE = 2'b10,
+ MACHINE_MODE = 2'b11
+ } privileged_mode_t;
+
+ typedef enum bit [4:0] {
+ RV32I,
+ RV64I,
+ RV32M,
+ RV64M,
+ RV32A,
+ RV64A,
+ RV32F,
+ RV32FC,
+ RV64F,
+ RV32D,
+ RV32DC,
+ RV64D,
+ RV32C,
+ RV64C,
+ RV128I,
+ RV128C
+ } riscv_instr_group_t;
+
+ typedef enum {
+ // RV32I instructions
+ LUI,
+ AUIPC,
+ JAL,
+ JALR,
+ BEQ,
+ BNE,
+ BLT,
+ BGE,
+ BLTU,
+ BGEU,
+ LB,
+ LH,
+ LW,
+ LBU,
+ LHU,
+ SB,
+ SH,
+ SW,
+ ADDI,
+ SLTI,
+ SLTIU,
+ XORI,
+ ORI,
+ ANDI,
+ SLLI,
+ SRLI,
+ SRAI,
+ ADD,
+ SUB,
+ SLL,
+ SLT,
+ SLTU,
+ XOR,
+ SRL,
+ SRA,
+ OR,
+ AND,
+ NOP,
+ FENCE,
+ FENCEI,
+ ECALL,
+ EBREAK,
+ CSRRW,
+ CSRRS,
+ CSRRC,
+ CSRRWI,
+ CSRRSI,
+ CSRRCI,
+ // RV32M instructions
+ MUL,
+ MULH,
+ MULHSU,
+ MULHU,
+ DIV,
+ DIVU,
+ REM,
+ REMU,
+ // RV64M instructions
+ MULW,
+ DIVW,
+ DIVUW,
+ REMW,
+ REMUW,
+ // RV32F instructions
+ FLW,
+ FSW,
+ FMADD_S,
+ FMSUB_S,
+ FNMSUB_S,
+ FNMADD_S,
+ FADD_S,
+ FSUB_S,
+ FMUL_S,
+ FDIV_S,
+ FSQRT_S,
+ FSGNJ_S,
+ FSGNJN_S,
+ FSGNJX_S,
+ FMIN_S,
+ FMAX_S,
+ FCVT_W_S,
+ FCVT_WU_S,
+ FMV_X_W,
+ FEQ_S,
+ FLT_S,
+ FLE_S,
+ FCLASS_S,
+ FCVT_S_W,
+ FCVT_S_WU,
+ FMV_W_X,
+ FCVT_L_S,
+ FCVT_LU_S,
+ FCVT_S_L,
+ FCVT_S_LU,
+ // RV64I
+ LWU,
+ LD,
+ SD,
+ ADDIW,
+ SLLIW,
+ SRLIW,
+ SRAIW,
+ ADDW,
+ SUBW,
+ SLLW,
+ SRLW,
+ SRAW,
+ // RV32C
+ C_LW,
+ C_SW,
+ C_LWSP,
+ C_SWSP,
+ C_ADDI4SPN,
+ C_ADDI,
+ C_LI,
+ C_ADDI16SP,
+ C_LUI,
+ C_SRLI,
+ C_SRAI,
+ C_ANDI,
+ C_SUB,
+ C_XOR,
+ C_OR,
+ C_AND,
+ C_BEQZ,
+ C_BNEZ,
+ C_SLLI,
+ C_MV,
+ C_EBREAK,
+ C_ADD,
+ C_NOP,
+ C_J,
+ C_JAL,
+ C_JR,
+ C_JALR,
+ // RV64C
+ C_ADDIW,
+ C_SUBW,
+ C_ADDW,
+ C_LD,
+ C_SD,
+ C_LDSP,
+ C_SDSP,
+ // RV128C
+ C_SRLI64,
+ C_SRAI64,
+ C_SLLI64,
+ C_LQ,
+ C_SQ,
+ C_LQSP,
+ C_SQSP,
+ // RV32FC
+ C_FLW,
+ C_FSW,
+ C_FLWSP,
+ C_FSWSP,
+ // RV32DC
+ C_FLD,
+ C_FSD,
+ C_FLDSP,
+ C_FSDSP,
+ // Supervisor instruction
+ MRET,
+ URET,
+ SRET,
+ WFI,
+ SFENCE_VMA,
+ // You can add other instructions here
+ INVALID_INSTR
+ } riscv_instr_name_t;
+
+ `include "riscv_core_setting.sv"
+
+ // Maximum virtual address bits used by the program
+ parameter MAX_USED_VADDR_BITS = 30;
+
+ // xSTATUS bit mask
+ parameter bit [XLEN - 1 : 0] MPRV_BIT_MASK = 'h1 << 17;
+ parameter bit [XLEN - 1 : 0] SUM_BIT_MASK = 'h1 << 18;
+ parameter bit [XLEN - 1 : 0] MPP_BIT_MASK = 'h3 << 11;
+
+ typedef enum bit [4:0] {
+ ZERO = 5'b00000,
+ RA,
+ SP,
+ GP,
+ TP,
+ T0,
+ T1,
+ T2,
+ S0,
+ S1,
+ A0,
+ A1,
+ A2,
+ A3,
+ A4,
+ A5,
+ A6,
+ A7,
+ S2,
+ S3,
+ S4,
+ S5,
+ S6,
+ S7,
+ S8,
+ S9,
+ S10,
+ S11,
+ T3,
+ T4,
+ T5,
+ T6
+ } riscv_reg_t;
+
+ typedef enum bit [3:0] {
+ J_FORMAT = 0,
+ U_FORMAT,
+ I_FORMAT,
+ B_FORMAT,
+ R_FORMAT,
+ S_FORMAT,
+ CI_FORMAT,
+ CB_FORMAT,
+ CJ_FORMAT,
+ CR_FORMAT,
+ CL_FORMAT,
+ CS_FORMAT,
+ CSS_FORMAT,
+ CIW_FORMAT
+ } riscv_instr_format_t;
+
+ typedef enum bit [3:0] {
+ LOAD = 0,
+ STORE,
+ SHIFT,
+ ARITHMETIC,
+ LOGICAL,
+ COMPARE,
+ BRANCH,
+ JUMP,
+ SYNCH,
+ SYSTEM,
+ COUNTER,
+ CSR,
+ CHANGELEVEL,
+ TRAP,
+ INTERRUPT
+ } riscv_instr_cateogry_t;
+
+ typedef bit [11:0] riscv_csr_t;
+
+ typedef enum bit [11:0] {
+ // User mode register
+ USTATUS = 'h000, // User status
+ UIE = 'h004, // User interrupt-enable register
+ UTVEC = 'h005, // User trap-handler base address
+ USCRATCH = 'h040, // Scratch register for user trap handlers
+ UEPC = 'h041, // User exception program counter
+ UCAUSE = 'h042, // User trap cause
+ UTVAL = 'h043, // User bad address or instruction
+ UIP = 'h044, // User interrupt pending
+ FFLAGS = 'h001, // Floating-Point Accrued Exceptions
+ FRM = 'h002, // Floating-Point Dynamic Rounding Mode
+ FCSR = 'h003, // Floating-Point Control/Status Register (FRM + FFLAGS)
+ CYCLE = 'hC00, // Cycle counter for RDCYCLE instruction
+ TIME = 'hC01, // Timer for RDTIME instruction
+ INSTRET = 'hC02, // Instructions-retired counter for RDINSTRET instruction
+ HPMCOUNTER3 = 'hC03, // Performance-monitoring counter
+ HPMCOUNTER4 = 'hC04, // Performance-monitoring counter
+ HPMCOUNTER5 = 'hC05, // Performance-monitoring counter
+ HPMCOUNTER6 = 'hC06, // Performance-monitoring counter
+ HPMCOUNTER7 = 'hC07, // Performance-monitoring counter
+ HPMCOUNTER8 = 'hC08, // Performance-monitoring counter
+ HPMCOUNTER9 = 'hC09, // Performance-monitoring counter
+ HPMCOUNTER10 = 'hC0A, // Performance-monitoring counter
+ HPMCOUNTER11 = 'hC0B, // Performance-monitoring counter
+ HPMCOUNTER12 = 'hC0C, // Performance-monitoring counter
+ HPMCOUNTER13 = 'hC0D, // Performance-monitoring counter
+ HPMCOUNTER14 = 'hC0E, // Performance-monitoring counter
+ HPMCOUNTER15 = 'hC0F, // Performance-monitoring counter
+ HPMCOUNTER16 = 'hC10, // Performance-monitoring counter
+ HPMCOUNTER17 = 'hC11, // Performance-monitoring counter
+ HPMCOUNTER18 = 'hC12, // Performance-monitoring counter
+ HPMCOUNTER19 = 'hC13, // Performance-monitoring counter
+ HPMCOUNTER20 = 'hC14, // Performance-monitoring counter
+ HPMCOUNTER21 = 'hC15, // Performance-monitoring counter
+ HPMCOUNTER22 = 'hC16, // Performance-monitoring counter
+ HPMCOUNTER23 = 'hC17, // Performance-monitoring counter
+ HPMCOUNTER24 = 'hC18, // Performance-monitoring counter
+ HPMCOUNTER25 = 'hC19, // Performance-monitoring counter
+ HPMCOUNTER26 = 'hC1A, // Performance-monitoring counter
+ HPMCOUNTER27 = 'hC1B, // Performance-monitoring counter
+ HPMCOUNTER28 = 'hC1C, // Performance-monitoring counter
+ HPMCOUNTER29 = 'hC1D, // Performance-monitoring counter
+ HPMCOUNTER30 = 'hC1E, // Performance-monitoring counter
+ HPMCOUNTER31 = 'hC1F, // Performance-monitoring counter
+ CYCLEH = 'hC80, // Upper 32 bits of CYCLE, RV32I only
+ TIMEH = 'hC81, // Upper 32 bits of TIME, RV32I only
+ INSTRETH = 'hC82, // Upper 32 bits of INSTRET, RV32I only
+ HPMCOUNTER3H = 'hC83, // Upper 32 bits of HPMCOUNTER3, RV32I only
+ HPMCOUNTER4H = 'hC84, // Upper 32 bits of HPMCOUNTER4, RV32I only
+ HPMCOUNTER5H = 'hC85, // Upper 32 bits of HPMCOUNTER5, RV32I only
+ HPMCOUNTER6H = 'hC86, // Upper 32 bits of HPMCOUNTER6, RV32I only
+ HPMCOUNTER7H = 'hC87, // Upper 32 bits of HPMCOUNTER7, RV32I only
+ HPMCOUNTER8H = 'hC88, // Upper 32 bits of HPMCOUNTER8, RV32I only
+ HPMCOUNTER9H = 'hC89, // Upper 32 bits of HPMCOUNTER9, RV32I only
+ HPMCOUNTER10H = 'hC8A, // Upper 32 bits of HPMCOUNTER10, RV32I only
+ HPMCOUNTER11H = 'hC8B, // Upper 32 bits of HPMCOUNTER11, RV32I only
+ HPMCOUNTER12H = 'hC8C, // Upper 32 bits of HPMCOUNTER12, RV32I only
+ HPMCOUNTER13H = 'hC8D, // Upper 32 bits of HPMCOUNTER13, RV32I only
+ HPMCOUNTER14H = 'hC8E, // Upper 32 bits of HPMCOUNTER14, RV32I only
+ HPMCOUNTER15H = 'hC8F, // Upper 32 bits of HPMCOUNTER15, RV32I only
+ HPMCOUNTER16H = 'hC90, // Upper 32 bits of HPMCOUNTER16, RV32I only
+ HPMCOUNTER17H = 'hC91, // Upper 32 bits of HPMCOUNTER17, RV32I only
+ HPMCOUNTER18H = 'hC92, // Upper 32 bits of HPMCOUNTER18, RV32I only
+ HPMCOUNTER19H = 'hC93, // Upper 32 bits of HPMCOUNTER19, RV32I only
+ HPMCOUNTER20H = 'hC94, // Upper 32 bits of HPMCOUNTER20, RV32I only
+ HPMCOUNTER21H = 'hC95, // Upper 32 bits of HPMCOUNTER21, RV32I only
+ HPMCOUNTER22H = 'hC96, // Upper 32 bits of HPMCOUNTER22, RV32I only
+ HPMCOUNTER23H = 'hC97, // Upper 32 bits of HPMCOUNTER23, RV32I only
+ HPMCOUNTER24H = 'hC98, // Upper 32 bits of HPMCOUNTER24, RV32I only
+ HPMCOUNTER25H = 'hC99, // Upper 32 bits of HPMCOUNTER25, RV32I only
+ HPMCOUNTER26H = 'hC9A, // Upper 32 bits of HPMCOUNTER26, RV32I only
+ HPMCOUNTER27H = 'hC9B, // Upper 32 bits of HPMCOUNTER27, RV32I only
+ HPMCOUNTER28H = 'hC9C, // Upper 32 bits of HPMCOUNTER28, RV32I only
+ HPMCOUNTER29H = 'hC9D, // Upper 32 bits of HPMCOUNTER29, RV32I only
+ HPMCOUNTER30H = 'hC9E, // Upper 32 bits of HPMCOUNTER30, RV32I only
+ HPMCOUNTER31H = 'hC9F, // Upper 32 bits of HPMCOUNTER31, RV32I only
+ // Supervisor mode register
+ SSTATUS = 'h100, // Supervisor status
+ SEDELEG = 'h102, // Supervisor exception delegation register
+ SIDELEG = 'h103, // Supervisor interrupt delegation register
+ SIE = 'h104, // Supervisor interrupt-enable register
+ STVEC = 'h105, // Supervisor trap-handler base address
+ SCOUNTEREN = 'h106, // Supervisor counter enable
+ SSCRATCH = 'h140, // Scratch register for supervisor trap handlers
+ SEPC = 'h141, // Supervisor exception program counter
+ SCAUSE = 'h142, // Supervisor trap cause
+ STVAL = 'h143, // Supervisor bad address or instruction
+ SIP = 'h144, // Supervisor interrupt pending
+ SATP = 'h180, // Supervisor address translation and protection
+ // Machine mode register
+ MVENDORID = 'hF11, // Vendor ID
+ MARCHID = 'hF12, // Architecture ID
+ MIMPID = 'hF13, // Implementation ID
+ MHARTID = 'hF14, // Hardware thread ID
+ MSTATUS = 'h300, // Machine status
+ MISA = 'h301, // ISA and extensions
+ MEDELEG = 'h302, // Machine exception delegation register
+ MIDELEG = 'h303, // Machine interrupt delegation register
+ MIE = 'h304, // Machine interrupt-enable register
+ MTVEC = 'h305, // Machine trap-handler base address
+ MCOUNTEREN = 'h306, // Machine counter enable
+ MSCRATCH = 'h340, // Scratch register for machine trap handlers
+ MEPC = 'h341, // Machine exception program counter
+ MCAUSE = 'h342, // Machine trap cause
+ MTVAL = 'h343, // Machine bad address or instruction
+ MIP = 'h344, // Machine interrupt pending
+ PMPCFG0 = 'h3A0, // Physical memory protection configuration
+ PMPCFG1 = 'h3A1, // Physical memory protection configuration, RV32 only
+ PMPCFG2 = 'h3A2, // Physical memory protection configuration
+ PMPCFG3 = 'h3A3, // Physical memory protection configuration, RV32 only
+ PMPADDR0 = 'h3B0, // Physical memory protection address register
+ PMPADDR1 = 'h3B1, // Physical memory protection address register
+ PMPADDR2 = 'h3B2, // Physical memory protection address register
+ PMPADDR3 = 'h3B3, // Physical memory protection address register
+ PMPADDR4 = 'h3B4, // Physical memory protection address register
+ PMPADDR5 = 'h3B5, // Physical memory protection address register
+ PMPADDR6 = 'h3B6, // Physical memory protection address register
+ PMPADDR7 = 'h3B7, // Physical memory protection address register
+ PMPADDR8 = 'h3B8, // Physical memory protection address register
+ PMPADDR9 = 'h3B9, // Physical memory protection address register
+ PMPADDR10 = 'h3BA, // Physical memory protection address register
+ PMPADDR11 = 'h3BB, // Physical memory protection address register
+ PMPADDR12 = 'h3BC, // Physical memory protection address register
+ PMPADDR13 = 'h3BD, // Physical memory protection address register
+ PMPADDR14 = 'h3BE, // Physical memory protection address register
+ PMPADDR15 = 'h3BF, // Physical memory protection address register
+ MCYCLE = 'hB00, // Machine cycle counter
+ MINSTRET = 'hB02, // Machine instructions-retired counter
+ MHPMCOUNTER3 = 'hB03, // Machine performance-monitoring counter
+ MHPMCOUNTER4 = 'hB04, // Machine performance-monitoring counter
+ MHPMCOUNTER5 = 'hB05, // Machine performance-monitoring counter
+ MHPMCOUNTER6 = 'hB06, // Machine performance-monitoring counter
+ MHPMCOUNTER7 = 'hB07, // Machine performance-monitoring counter
+ MHPMCOUNTER8 = 'hB08, // Machine performance-monitoring counter
+ MHPMCOUNTER9 = 'hB09, // Machine performance-monitoring counter
+ MHPMCOUNTER10 = 'hB0A, // Machine performance-monitoring counter
+ MHPMCOUNTER11 = 'hB0B, // Machine performance-monitoring counter
+ MHPMCOUNTER12 = 'hB0C, // Machine performance-monitoring counter
+ MHPMCOUNTER13 = 'hB0D, // Machine performance-monitoring counter
+ MHPMCOUNTER14 = 'hB0E, // Machine performance-monitoring counter
+ MHPMCOUNTER15 = 'hB0F, // Machine performance-monitoring counter
+ MHPMCOUNTER16 = 'hB10, // Machine performance-monitoring counter
+ MHPMCOUNTER17 = 'hB11, // Machine performance-monitoring counter
+ MHPMCOUNTER18 = 'hB12, // Machine performance-monitoring counter
+ MHPMCOUNTER19 = 'hB13, // Machine performance-monitoring counter
+ MHPMCOUNTER20 = 'hB14, // Machine performance-monitoring counter
+ MHPMCOUNTER21 = 'hB15, // Machine performance-monitoring counter
+ MHPMCOUNTER22 = 'hB16, // Machine performance-monitoring counter
+ MHPMCOUNTER23 = 'hB17, // Machine performance-monitoring counter
+ MHPMCOUNTER24 = 'hB18, // Machine performance-monitoring counter
+ MHPMCOUNTER25 = 'hB19, // Machine performance-monitoring counter
+ MHPMCOUNTER26 = 'hB1A, // Machine performance-monitoring counter
+ MHPMCOUNTER27 = 'hB1B, // Machine performance-monitoring counter
+ MHPMCOUNTER28 = 'hB1C, // Machine performance-monitoring counter
+ MHPMCOUNTER29 = 'hB1D, // Machine performance-monitoring counter
+ MHPMCOUNTER30 = 'hB1E, // Machine performance-monitoring counter
+ MHPMCOUNTER31 = 'hB1F, // Machine performance-monitoring counter
+ MCYCLEH = 'hB80, // Upper 32 bits of MCYCLE, RV32I only
+ MINSTRETH = 'hB82, // Upper 32 bits of MINSTRET, RV32I only
+ MHPMCOUNTER3H = 'hB83, // Upper 32 bits of HPMCOUNTER3, RV32I only
+ MHPMCOUNTER4H = 'hB84, // Upper 32 bits of HPMCOUNTER4, RV32I only
+ MHPMCOUNTER5H = 'hB85, // Upper 32 bits of HPMCOUNTER5, RV32I only
+ MHPMCOUNTER6H = 'hB86, // Upper 32 bits of HPMCOUNTER6, RV32I only
+ MHPMCOUNTER7H = 'hB87, // Upper 32 bits of HPMCOUNTER7, RV32I only
+ MHPMCOUNTER8H = 'hB88, // Upper 32 bits of HPMCOUNTER8, RV32I only
+ MHPMCOUNTER9H = 'hB89, // Upper 32 bits of HPMCOUNTER9, RV32I only
+ MHPMCOUNTER10H = 'hB8A, // Upper 32 bits of HPMCOUNTER10, RV32I only
+ MHPMCOUNTER11H = 'hB8B, // Upper 32 bits of HPMCOUNTER11, RV32I only
+ MHPMCOUNTER12H = 'hB8C, // Upper 32 bits of HPMCOUNTER12, RV32I only
+ MHPMCOUNTER13H = 'hB8D, // Upper 32 bits of HPMCOUNTER13, RV32I only
+ MHPMCOUNTER14H = 'hB8E, // Upper 32 bits of HPMCOUNTER14, RV32I only
+ MHPMCOUNTER15H = 'hB8F, // Upper 32 bits of HPMCOUNTER15, RV32I only
+ MHPMCOUNTER16H = 'hB90, // Upper 32 bits of HPMCOUNTER16, RV32I only
+ MHPMCOUNTER17H = 'hB91, // Upper 32 bits of HPMCOUNTER17, RV32I only
+ MHPMCOUNTER18H = 'hB92, // Upper 32 bits of HPMCOUNTER18, RV32I only
+ MHPMCOUNTER19H = 'hB93, // Upper 32 bits of HPMCOUNTER19, RV32I only
+ MHPMCOUNTER20H = 'hB94, // Upper 32 bits of HPMCOUNTER20, RV32I only
+ MHPMCOUNTER21H = 'hB95, // Upper 32 bits of HPMCOUNTER21, RV32I only
+ MHPMCOUNTER22H = 'hB96, // Upper 32 bits of HPMCOUNTER22, RV32I only
+ MHPMCOUNTER23H = 'hB97, // Upper 32 bits of HPMCOUNTER23, RV32I only
+ MHPMCOUNTER24H = 'hB98, // Upper 32 bits of HPMCOUNTER24, RV32I only
+ MHPMCOUNTER25H = 'hB99, // Upper 32 bits of HPMCOUNTER25, RV32I only
+ MHPMCOUNTER26H = 'hB9A, // Upper 32 bits of HPMCOUNTER26, RV32I only
+ MHPMCOUNTER27H = 'hB9B, // Upper 32 bits of HPMCOUNTER27, RV32I only
+ MHPMCOUNTER28H = 'hB9C, // Upper 32 bits of HPMCOUNTER28, RV32I only
+ MHPMCOUNTER29H = 'hB9D, // Upper 32 bits of HPMCOUNTER29, RV32I only
+ MHPMCOUNTER30H = 'hB9E, // Upper 32 bits of HPMCOUNTER30, RV32I only
+ MHPMCOUNTER31H = 'hB9F, // Upper 32 bits of HPMCOUNTER31, RV32I only
+ MHPMEVENT3 = 'h323, // Machine performance-monitoring event selector
+ MHPMEVENT4 = 'h324, // Machine performance-monitoring event selector
+ MHPMEVENT5 = 'h325, // Machine performance-monitoring event selector
+ MHPMEVENT6 = 'h326, // Machine performance-monitoring event selector
+ MHPMEVENT7 = 'h327, // Machine performance-monitoring event selector
+ MHPMEVENT8 = 'h328, // Machine performance-monitoring event selector
+ MHPMEVENT9 = 'h329, // Machine performance-monitoring event selector
+ MHPMEVENT10 = 'h32A, // Machine performance-monitoring event selector
+ MHPMEVENT11 = 'h32B, // Machine performance-monitoring event selector
+ MHPMEVENT12 = 'h32C, // Machine performance-monitoring event selector
+ MHPMEVENT13 = 'h32D, // Machine performance-monitoring event selector
+ MHPMEVENT14 = 'h32E, // Machine performance-monitoring event selector
+ MHPMEVENT15 = 'h32F, // Machine performance-monitoring event selector
+ MHPMEVENT16 = 'h330, // Machine performance-monitoring event selector
+ MHPMEVENT17 = 'h331, // Machine performance-monitoring event selector
+ MHPMEVENT18 = 'h332, // Machine performance-monitoring event selector
+ MHPMEVENT19 = 'h333, // Machine performance-monitoring event selector
+ MHPMEVENT20 = 'h334, // Machine performance-monitoring event selector
+ MHPMEVENT21 = 'h335, // Machine performance-monitoring event selector
+ MHPMEVENT22 = 'h336, // Machine performance-monitoring event selector
+ MHPMEVENT23 = 'h337, // Machine performance-monitoring event selector
+ MHPMEVENT24 = 'h338, // Machine performance-monitoring event selector
+ MHPMEVENT25 = 'h339, // Machine performance-monitoring event selector
+ MHPMEVENT26 = 'h33A, // Machine performance-monitoring event selector
+ MHPMEVENT27 = 'h33B, // Machine performance-monitoring event selector
+ MHPMEVENT28 = 'h33C, // Machine performance-monitoring event selector
+ MHPMEVENT29 = 'h33D, // Machine performance-monitoring event selector
+ MHPMEVENT30 = 'h33E, // Machine performance-monitoring event selector
+ MHPMEVENT31 = 'h33F, // Machine performance-monitoring event selector
+ TSELECT = 'h7A0, // Debug/Trace trigger register select
+ TDATA1 = 'h7A1, // First Debug/Trace trigger data register
+ TDATA2 = 'h7A2, // Second Debug/Trace trigger data register
+ TDATA3 = 'h7A3, // Third Debug/Trace trigger data register
+ DCSR = 'h7B0, // Debug control and status register
+ DPC = 'h7B1, // Debug PC
+ DSCRATCH = 'h7B2 // Debug scratch register
+ } privileged_reg_t;
+
+ typedef enum bit [5:0] {
+ RSVD, // Reserved field
+ MXL, // mis.mxl
+ EXTENSION, // mis.extension
+ MODE, // satp.mode
+ ASID, // satp.asid
+ PPN // satp.ppn
+ } privileged_reg_fld_t;
+
+ typedef enum bit [1:0] {
+ M_LEVEL = 2'b11, // Machine mode
+ S_LEVEL = 2'b01, // Supervisor mode
+ U_LEVEL = 2'b00 // User mode
+ } privileged_level_t;
+
+ typedef enum bit [1:0] {
+ WIRI, // Reserved Writes Ignored, Reads Ignore Value
+ WPRI, // Reserved Writes Preserve Values, Reads Ignore Value
+ WLRL, // Write/Read Only Legal Values
+ WARL // Write Any Values, Reads Legal Values
+ } reg_field_access_t;
+
+ //Pseudo instructions
+ typedef enum bit [7:0] {
+ LI = 0,
+ LA
+ } riscv_pseudo_instr_name_t;
+
+ // Data pattern of the memory model
+ typedef enum bit [1:0] {
+ RAND_DATA = 0,
+ ALL_ZERO,
+ INCR_VAL
+ } data_pattern_t;
+
+ typedef enum bit [2:0] {
+ NEXT_LEVEL_PAGE = 3'b000, // Pointer to next level of page table.
+ READ_ONLY_PAGE = 3'b001, // Read-only page.
+ READ_WRITE_PAGE = 3'b011, // Read-write page.
+ EXECUTE_ONLY_PAGE = 3'b100, // Execute-only page.
+ READ_EXECUTE_PAGE = 3'b101, // Read-execute page.
+ R_W_EXECUTE_PAGE = 3'b111 // Read-write-execute page
+ } pte_permission_t;
+
+ typedef enum bit [3:0] {
+ U_SOFTWARE_INTR = 4'h0,
+ S_SOFTWARE_INTR = 4'h1,
+ M_SOFTWARE_INTR = 4'h3,
+ U_TIMER_INTR = 4'h4,
+ S_TIMER_INTR = 4'h5,
+ M_TIMER_INTR = 4'h7,
+ U_EXTERNAL_INTR = 4'h8,
+ S_EXTERNAL_INTR = 4'h9,
+ M_EXTERNAL_INTR = 4'hB
+ } interrupt_cause_t;
+
+ typedef enum bit [3:0] {
+ INSTRUCTION_ADDRESS_MISALIGNED = 4'h0,
+ INSTRUCTION_ACCESS_FAULT = 4'h1,
+ ILLEGAL_INSTRUCTION = 4'h2,
+ BREAKPOINT = 4'h3,
+ LOAD_ADDRESS_MISALIGNED = 4'h4,
+ LOAD_ACCESS_FAULT = 4'h5,
+ STORE_AMO_ADDRESS_MISALIGNED = 4'h6,
+ STORE_AMO_ACCESS_FAULT = 4'h7,
+ ECALL_UMODE = 4'h8,
+ ECALL_SMODE = 4'h9,
+ ECALL_MMODE = 4'hB,
+ INSTRUCTION_PAGE_FAULT = 4'hC,
+ LOAD_PAGE_FAULT = 4'hD,
+ STORE_AMO_PAGE_FAULT = 4'hF
+ } exception_cause_t;
+
+ typedef bit [15:0] program_id_t;
+
+ parameter IMM25_WIDTH = 25;
+ parameter IMM12_WIDTH = 12;
+ parameter INSTR_WIDTH = 32;
+ parameter DATA_WIDTH = 32;
+
+ // Parameters for output assembly program formatting
+ parameter MAX_INSTR_STR_LEN = 11;
+ parameter LABEL_STR_LEN = 18;
+
+ // Parameter for program generation
+ parameter MAX_CALLSTACK_DEPTH = 20;
+ parameter MAX_SUB_PROGRAM_CNT = 20;
+ parameter MAX_CALL_PER_FUNC = 5;
+
+ string indent = {LABEL_STR_LEN{" "}};
+
+ // Format the string to a fixed length
+ function automatic string format_string(string str, int len = 10);
+ string formatted_str;
+ formatted_str = {len{" "}};
+ if(len < str.len()) return str;
+ formatted_str = {str, formatted_str.substr(0, len - str.len() - 1)};
+ return formatted_str;
+ endfunction
+
+ // Print the data in the following format
+ // 0xabcd, 0x1234, 0x3334 ...
+ function automatic string format_data(bit [7:0] data[], int unsigned byte_per_group = 4);
+ string str;
+ int cnt;
+ str = "0x";
+ foreach(data[i]) begin
+ if((i % byte_per_group == 0) && (i != data.size() - 1) && (i != 0)) begin
+ str = {str, ", 0x"};
+ end
+ str = {str, $sformatf("%2x", data[i])};
+ end
+ return str;
+ endfunction
+
+ // Get the instr name enum from a string
+ function automatic riscv_instr_name_t get_instr_name(string str);
+ riscv_instr_name_t instr = instr.first;
+ forever begin
+ if(str.toupper() == instr.name()) begin
+ return instr;
+ end
+ if(instr == instr.last) begin
+ return INVALID_INSTR;
+ end
+ instr = instr.next;
+ end
+ endfunction
+
+ // Push general purpose register to stack, this is needed before trap handling
+ function automatic void push_gpr_to_kernel_stack(privileged_reg_t status,
+ privileged_reg_t scratch,
+ bit mprv,
+ ref string instr[$]);
+ string store_instr = (XLEN == 32) ? "sw" : "sd";
+ // 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));
+ // Move TP to SP
+ instr.push_back("add sp, tp, zero");
+ // If MPRV is set and MPP is S/U mode, it means the address translation and memory protection
+ // for load/store instruction is the same as the mode indicated by MPP. In this case, we
+ // need to use the virtual address to access the kernel stack.
+ if((status == MSTATUS) && (SATP_MODE != BARE)) begin
+ // We temporarily use tp to check mstatus to avoid changing other GPR. The value of sp has
+ // been saved to xStatus 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
+ // 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));
+ end
+ end
+ // Reserve space from kernel stack to save all 32 GPR
+ instr.push_back($sformatf("1: addi sp, sp, -%0d", 32 * (XLEN/8)));
+ // Push all GPRs to kernel stack
+ for(int i = 0; i < 32; i++) begin
+ instr.push_back($sformatf("%0s x%0d, %0d(sp)", store_instr, i, i * (XLEN/8)));
+ end
+ endfunction
+
+ // Pop general purpose register from stack, this is needed before returning to user program
+ function automatic void pop_gpr_from_kernel_stack(privileged_reg_t status,
+ privileged_reg_t scratch,
+ bit mprv,
+ ref string instr[$]);
+ string load_instr = (XLEN == 32) ? "lw" : "ld";
+ // Pop user mode GPRs from kernel stack
+ for(int i = 0; i < 32; i++) begin
+ instr.push_back($sformatf("%0s x%0d, %0d(sp)", load_instr, i, i * (XLEN/8)));
+ end
+ // Restore kernel stack pointer
+ instr.push_back($sformatf("addi sp, sp, %0d", 32 * (XLEN/8)));
+ // Move SP to TP
+ instr.push_back("add tp, sp, zero");
+ // Restore user mode stack pointer
+ instr.push_back($sformatf("csrrw sp, 0x%0x, sp", scratch));
+ endfunction
+
+ `include "riscv_instr_gen_config.sv"
+ `include "riscv_illegal_instr.sv"
+ `include "riscv_reg.sv"
+ `include "riscv_privil_reg.sv"
+ `include "riscv_page_table_entry.sv"
+ `include "riscv_page_table_exception_cfg.sv"
+ `include "riscv_page_table.sv"
+ `include "riscv_page_table_list.sv"
+ `include "riscv_privileged_common_seq.sv"
+ `include "riscv_callstack_gen.sv"
+ `include "riscv_instr_base.sv"
+ `include "riscv_data_page_gen.sv"
+ `include "riscv_rand_instr.sv"
+ `include "riscv_instr_stream.sv"
+ `include "riscv_loop_instr.sv"
+ `include "riscv_directed_instr_lib.sv"
+ `include "riscv_load_store_instr_lib.sv"
+ `include "riscv_instr_sequence.sv"
+ `include "riscv_asm_program_gen.sv"
+
+endpackage
diff --git a/vendor/google_riscv-dv/src/riscv_instr_sequence.sv b/vendor/google_riscv-dv/src/riscv_instr_sequence.sv
new file mode 100644
index 00000000..cfffbf90
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_instr_sequence.sv
@@ -0,0 +1,321 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//-----------------------------------------------------------------------------------------
+// RISC-V instruction sequence
+//
+// This class is used to generate a single instruction sequence for a RISC-V assembly program.
+// It's used by riscv_asm_program_gen to generate the main program and all sub-programs. The
+// flow is explained below:
+// For main program:
+// - Generate instruction sequence body.
+// - Post-process the load/store/branch instructions.
+// - Insert the jump instructions to its sub-programs (done by riscv_asm_program_gen).
+// For sub program:
+// - Generate the stack push instructions which are executed when entering this program.
+// - Generate instruction sequence body.
+// - Generate the stack pop instructions which are executed before exiting this program.
+// - Post-process the load/store/branch instructions.
+// - Insert the jump instructions to its sub-programs (done by riscv_asm_program_gen).
+// - Generate a return instruction at the end of the program.
+//-----------------------------------------------------------------------------------------
+
+class riscv_instr_sequence extends uvm_sequence;
+
+ int unsigned instr_cnt; // Instruction count of this sequence
+ riscv_push_stack_instr instr_stack_enter; // Stack push instructions for sub-programs
+ riscv_pop_stack_instr instr_stack_exit; // Stack pop instructions for sub-programs
+ riscv_rand_instr_stream instr_stream; // Main instruction streams
+ bit is_main_program; // Type of this sequence (main or sub program)
+ string label_name; // Label of the sequence (program name)
+ riscv_instr_gen_config cfg; // Configuration class handle
+ string instr_string_list[$]; // Save the instruction list in string format
+ int program_stack_len; // Stack space allocated for this program
+ riscv_instr_stream directed_instr[]; // List of all directed instruction stream
+ riscv_illegal_instr illegal_instr; // Illegal instruction generator
+ int illegal_instr_pct; // Percentage of illegal instruction
+ bit enable_hint_instr; // Enable HINT instruction
+ int hint_instr_pct; // Percentage of HINT instruction
+
+ `uvm_object_utils(riscv_instr_sequence)
+
+ function new (string name = "");
+ super.new(name);
+ if(!uvm_config_db#(riscv_instr_gen_config)::get(null, "*", "instr_cfg", cfg))
+ `uvm_fatal(get_full_name(), "Cannot get instr_gen_cfg")
+ instr_stream = riscv_rand_instr_stream::type_id::create("instr_stream");
+ instr_stack_enter = riscv_push_stack_instr::type_id::create("instr_stack_enter");
+ instr_stack_exit = riscv_pop_stack_instr::type_id::create("instr_stack_exit");
+ illegal_instr = riscv_illegal_instr::type_id::create("illegal_instr");
+ endfunction
+
+ // Main function to generate the instruction stream
+ // The main random instruction stream is generated by instr_stream.gen_instr(), which generates
+ // each instruction one by one with a separate randomization call. It's not done by a single
+ // randomization call for the entire instruction stream because this solution won't scale if
+ // we have hundreds of thousands of instructions to generate. The constraint solver slows down
+ // considerably as the instruction stream becomes longer. The downside is we cannot specify
+ // constraints between instructions. The way to solve it is to have a dedicated directed
+ // instruction stream for such scenarios, like hazard sequence.
+ virtual function void gen_instr(bit is_main_program, bit enable_hint_instr = 1'b0);
+ this.is_main_program = is_main_program;
+ instr_stream.cfg = cfg;
+ instr_stream.initialize_instr_list(instr_cnt);
+ `uvm_info(get_full_name(), $sformatf("Start generating %0d instruction",
+ instr_stream.instr_list.size()), UVM_LOW)
+ // Do not generate load/store instruction here
+ // The load/store instruction will be inserted as directed instruction stream
+ instr_stream.gen_instr(.no_load_store(1'b1), .enable_hint_instr(enable_hint_instr));
+ if(!is_main_program) begin
+ gen_stack_enter_instr();
+ gen_stack_exit_instr();
+ end
+ `uvm_info(get_full_name(), "Finishing instruction generation", UVM_LOW)
+ endfunction
+
+ // Generate the stack push operations for this program
+ // It pushes the necessary context to the stack like RA, T0,loop registers etc. The stack
+ // pointer(SP) is reduced by the amount the stack space allocated to this program.
+ function void gen_stack_enter_instr();
+ bit allow_branch = ((illegal_instr_pct > 0) || (hint_instr_pct > 0)) ? 1'b0 : 1'b1;
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(program_stack_len,
+ program_stack_len inside {[cfg.min_stack_len_per_program : cfg.max_stack_len_per_program]};
+ // Keep stack len word aligned to avoid unaligned load/store
+ program_stack_len % (XLEN/8) == 0;,
+ "Cannot randomize program_stack_len")
+ instr_stack_enter.cfg = cfg;
+ instr_stack_enter.push_start_label = {label_name, "_stack_p"};
+ instr_stack_enter.gen_push_stack_instr(program_stack_len, .allow_branch(allow_branch));
+ instr_stream.instr_list = {instr_stack_enter.instr_list, instr_stream.instr_list};
+ endfunction
+
+ // Recover the saved GPR from the stack
+ // Advance the stack pointer(SP) to release the allocated stack space.
+ function void gen_stack_exit_instr();
+ instr_stack_exit.cfg = cfg;
+ instr_stack_exit.gen_pop_stack_instr(
+ program_stack_len, instr_stack_enter.saved_regs);
+ instr_stream.instr_list = {instr_stream.instr_list, instr_stack_exit.instr_list};
+ endfunction
+
+ //----------------------------------------------------------------------------------------------
+ // Instruction post-process
+ //
+ // Post-process is required for branch instructions:
+ //
+ // - Need to assign a valid branch target. This is done by picking a random instruction label in
+ // this sequence and assigning to the branch instruction. All the non-atomic instructions
+ // will have a unique numeric label as the local branch target identifier.
+ // - The atomic instruction streams don't have labels except for the first instruction. This is
+ // to avoid branching into an atomic instruction stream which breaks its atomicy. The
+ // definition of an atomic instruction stream here is a sequence of instructions which must be
+ // executed in-order.
+ // - In this sequence, only forward branch is handled. The backward branch target is implemented
+ // in a dedicated loop instruction sequence. Randomly choosing a backward branch target could
+ // lead to dead loops in the absence of proper loop exiting conditions.
+ //
+ //----------------------------------------------------------------------------------------------
+ virtual function void post_process_instr();
+ int i;
+ int label_idx;
+ int branch_target[string];
+ // Insert directed instructions, it's randomly mixed with the random instruction stream.
+ foreach (directed_instr[i]) begin
+ instr_stream.insert_instr_stream(directed_instr[i].instr_list);
+ end
+ // Assign an index for all instructions, these indexes won't change even a new instruction
+ // is injected in the post process.
+ foreach (instr_stream.instr_list[i]) begin
+ instr_stream.instr_list[i].idx = label_idx;
+ if (instr_stream.instr_list[i].has_label && !instr_stream.instr_list[i].atomic) begin
+ if ((illegal_instr_pct > 0) && (instr_stream.instr_list[i].is_illegal_instr == 0)) begin
+ // The illegal instruction generator always increase PC by 4 when resume execution, need
+ // to make sure PC + 4 is at the correct instruction boundary.
+ if (instr_stream.instr_list[i].is_compressed) begin
+ if (i < instr_stream.instr_list.size()-1) begin
+ if (instr_stream.instr_list[i+1].is_compressed) begin
+ instr_stream.instr_list[i].is_illegal_instr =
+ ($urandom_range(0, 100) < illegal_instr_pct);
+ end
+ end
+ end else begin
+ instr_stream.instr_list[i].is_illegal_instr =
+ ($urandom_range(0, 100) < illegal_instr_pct);
+ end
+ end
+ if ((hint_instr_pct > 0) && (instr_stream.instr_list[i].is_illegal_instr == 0)) begin
+ if (instr_stream.instr_list[i].is_compressed) begin
+ instr_stream.instr_list[i].is_hint_instr =
+ ($urandom_range(0, 100) < hint_instr_pct);
+ end
+ end
+ instr_stream.instr_list[i].label = $sformatf("%0d", label_idx);
+ instr_stream.instr_list[i].is_local_numeric_label = 1'b1;
+ label_idx++;
+ end
+ end
+ // Generate branch target
+ while(i < instr_stream.instr_list.size()) begin
+ if((instr_stream.instr_list[i].category == BRANCH) &&
+ (!instr_stream.instr_list[i].branch_assigned) &&
+ (!instr_stream.instr_list[i].is_illegal_instr)) begin
+ // Post process the branch instructions to give a valid local label
+ // Here we only allow forward branch to avoid unexpected infinite loop
+ // The loop structure will be inserted with a separate routine using
+ // reserved loop registers
+ int branch_target_label;
+ int branch_byte_offset;
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(branch_target_label,
+ branch_target_label >= instr_stream.instr_list[i].idx+1;
+ branch_target_label <= label_idx-1;
+ branch_target_label <= instr_stream.instr_list[i].idx+cfg.max_branch_step;,
+ "Cannot randomize branch_target_label")
+ `uvm_info(get_full_name(),
+ $sformatf("Processing branch instruction[%0d]:%0s # %0d -> %0d",
+ i, instr_stream.instr_list[i].convert2asm(),
+ instr_stream.instr_list[i].idx, branch_target_label), UVM_HIGH)
+ instr_stream.instr_list[i].imm_str = $sformatf("%0df", branch_target_label);
+ // Below calculation is only needed for generating the instruction stream in binary format
+ for (int j = i + 1; j < instr_stream.instr_list.size(); j++) begin
+ branch_byte_offset = (instr_stream.instr_list[j-1].is_compressed) ?
+ branch_byte_offset + 2 : branch_byte_offset + 4;
+ if (instr_stream.instr_list[j].label == $sformatf("%0d", branch_target_label)) begin
+ instr_stream.instr_list[i].imm = branch_byte_offset;
+ break;
+ end else if (j == instr_stream.instr_list.size() - 1) begin
+ `uvm_fatal(`gfn, $sformatf("Cannot find target label : %0d", branch_target_label))
+ end
+ end
+ instr_stream.instr_list[i].branch_assigned = 1'b1;
+ branch_target[branch_target_label] = 1;
+ end
+ // Remove the local label which is not used as branch target
+ if(instr_stream.instr_list[i].has_label &&
+ instr_stream.instr_list[i].is_local_numeric_label) begin
+ int idx = instr_stream.instr_list[i].label.atoi();
+ if(!branch_target[idx]) begin
+ instr_stream.instr_list[i].has_label = 1'b0;
+ end
+ end
+ i++;
+ end
+ `uvm_info(get_full_name(), "Finished post-processing instructions", UVM_HIGH)
+ endfunction
+
+ // Inject a jump instruction stream
+ // This function is called by riscv_asm_program_gen with the target program label
+ // The jump routine is implmented with an atomic instruction stream(riscv_jump_instr). Similar
+ // to load/store instructions, JALR/JAL instructions also need a proper base address and offset
+ // as the jump target.
+ function void insert_jump_instr(string target_label, int idx);
+ riscv_jump_instr jump_instr;
+ jump_instr = riscv_jump_instr::type_id::create("jump_instr");
+ jump_instr.target_program_label = target_label;
+ if(!is_main_program)
+ jump_instr.stack_exit_instr = instr_stack_exit.pop_stack_instr;
+ jump_instr.cfg = cfg;
+ jump_instr.label = label_name;
+ jump_instr.idx = idx;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr,
+ if(is_main_program) {
+ jump_instr.jump.rd == RA;
+ },
+ "Cannot randomize jump_instr")
+ `uvm_info(get_full_name(), $sformatf("%0s -> %0s",
+ jump_instr.jump.instr_name.name(), label_name), UVM_HIGH)
+ instr_stream.insert_instr_stream(jump_instr.instr_list);
+ endfunction
+
+ // Convert the instruction stream to the string format.
+ // Label is attached to the instruction if available, otherwise attach proper space to make
+ // the code indent consistent.
+ function void generate_instr_stream();
+ string prefix, str;
+ int i;
+ instr_string_list = {};
+ for(i = 0; i < instr_stream.instr_list.size(); i++) begin
+ if(i == 0) begin
+ prefix = format_string($sformatf("%0s:", label_name), LABEL_STR_LEN);
+ instr_stream.instr_list[i].has_label = 1'b1;
+ end else begin
+ if(instr_stream.instr_list[i].has_label) begin
+ prefix = format_string($sformatf("%0s:", instr_stream.instr_list[i].label),
+ LABEL_STR_LEN);
+ end else begin
+ prefix = format_string(" ", LABEL_STR_LEN);
+ end
+ end
+ str = {prefix, instr_stream.instr_list[i].convert2asm()};
+ instr_string_list.push_back(str);
+ end
+ prefix = format_string($sformatf("%0d:", i), LABEL_STR_LEN);
+ if(!is_main_program) begin
+ str = {prefix, "ret"};
+ instr_string_list.push_back(str);
+ end
+ endfunction
+
+ // Convert the instruction stream to binary format
+ function void generate_binary_stream(ref string binary[$]);
+ string instr_bin;
+ string remaining_bin;
+ string str;
+ illegal_instr.cfg = cfg;
+ foreach (instr_stream.instr_list[i]) begin
+ if (instr_stream.instr_list[i].is_illegal_instr) begin
+ // Replace the original instruction with illegal instruction binary
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_instr,
+ exception != kHintInstr;
+ compressed == instr_stream.instr_list[i].is_compressed;)
+ str = illegal_instr.get_bin_str();
+ `uvm_info(`gfn, $sformatf("Inject %0s [%0d] %0s replaced with %0s",
+ illegal_instr.exception.name(), i,
+ instr_stream.instr_list[i].convert2bin() ,str), UVM_HIGH)
+ end else if (instr_stream.instr_list[i].is_hint_instr) begin
+ // Replace the original instruction with HINT instruction binary
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_instr,
+ exception == kHintInstr;
+ compressed == instr_stream.instr_list[i].is_compressed;)
+ str = illegal_instr.get_bin_str();
+ `uvm_info(`gfn, $sformatf("Inject %0s [%0d] %0s replaced with %0s",
+ illegal_instr.exception.name(), i,
+ instr_stream.instr_list[i].convert2bin() ,str), UVM_HIGH)
+ end else begin
+ str = instr_stream.instr_list[i].convert2bin();
+ end
+ instr_bin = {str, remaining_bin};
+ // Handle various instruction alignment
+ if (instr_bin.len() == 8) begin
+ binary.push_back({"0x", instr_bin});
+ remaining_bin = "";
+ end else if (instr_bin.len() == 12) begin
+ binary.push_back({"0x", instr_bin.substr(4, 11)});
+ remaining_bin = instr_bin.substr(0, 3);
+ end else if (instr_bin.len() == 4) begin
+ remaining_bin = instr_bin;
+ end else begin
+ `uvm_fatal(`gfn, $sformatf("Unexpected binary length :%0d", instr_bin.len()))
+ end
+ `uvm_info("BIN", $sformatf("%0s : %0s", instr_stream.instr_list[i].convert2bin(),
+ instr_stream.instr_list[i].convert2asm()), UVM_HIGH)
+ end
+ // Attach a C_NOP(0x0001) to make the last entry 32b
+ if (remaining_bin != "") begin
+ binary.push_back({"0x0001", remaining_bin});
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_instr_stream.sv b/vendor/google_riscv-dv/src/riscv_instr_stream.sv
new file mode 100644
index 00000000..77c21419
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_instr_stream.sv
@@ -0,0 +1,186 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// Base class for RISC-V instruction stream
+// A instruction stream here is a queue of RISC-V basic instructions.
+// This class also provides some functions to manipulate the instruction stream, like insert a new
+// instruction, mix two instruction streams etc.
+class riscv_instr_stream extends uvm_object;
+
+ rand riscv_instr_base instr_list[$];
+ int unsigned instr_cnt;
+ string label = "";
+
+ `uvm_object_utils(riscv_instr_stream)
+ `uvm_object_new
+
+ // Initialize the instruction stream, create each instruction instance
+ function void initialize_instr_list(int unsigned instr_cnt);
+ instr_list = {};
+ this.instr_cnt = instr_cnt;
+ create_instr_instance();
+ endfunction
+
+ virtual function void create_instr_instance();
+ riscv_instr_base instr;
+ for(int i = 0; i < instr_cnt; i++) begin
+ instr = riscv_instr_base::type_id::create($sformatf("instr_%0d", i));
+ instr_list.push_back(instr);
+ end
+ endfunction
+
+ // Insert an instruction to the existing instruction stream at the given index
+ // When index is -1, the instruction is injected at a random location
+ function void insert_instr(riscv_instr_base instr, int idx = -1);
+ int current_instr_cnt = instr_list.size();
+ if(idx == -1) begin
+ idx = $urandom_range(0, current_instr_cnt-1);
+ while(instr_list[idx].atomic) begin
+ idx = $urandom_range(0, current_instr_cnt-1);
+ end
+ end else if((idx > current_instr_cnt) || (idx < 0)) begin
+ `uvm_error(`gfn, $sformatf("Cannot insert instr:%0s at idx %0d",
+ instr.convert2asm(), idx))
+ end
+ instr_list.insert(idx, instr);
+ endfunction
+
+ // Insert an instruction to the existing instruction stream at the given index
+ // When index is -1, the instruction is injected at a random location
+ // When replace is 1, the original instruction at the inserted position will be replaced
+ function void insert_instr_stream(riscv_instr_base new_instr[], int idx = -1, bit replace = 1'b0);
+ int current_instr_cnt = instr_list.size();
+ int new_instr_cnt = new_instr.size();
+ if(idx == -1) begin
+ idx = $urandom_range(0, current_instr_cnt-1);
+ while(instr_list[idx].atomic) begin
+ idx = $urandom_range(0, current_instr_cnt-1);
+ end
+ end else if((idx > current_instr_cnt) || (idx < 0)) begin
+ `uvm_error(`gfn, $sformatf("Cannot insert instr stream at idx %0d", idx))
+ end
+ // When replace is 1, the original instruction at this index will be removed. The label of the
+ // original instruction will be copied to the head of inserted instruction stream.
+ if(replace) begin
+ new_instr[0].label = instr_list[idx].label;
+ new_instr[0].has_label = instr_list[idx].has_label;
+ instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx+1:current_instr_cnt-1]};
+ end else begin
+ instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx:current_instr_cnt-1]};
+ end
+ endfunction
+
+ // Mix the input instruction stream with the original instruction, the instruction order is
+ // preserved. When 'contained' is set, the original instruction stream will be inside the
+ // new instruction stream with the first and last instruction from the input instruction stream.
+ function void mix_instr_stream(riscv_instr_base new_instr[], bit contained = 1'b0);
+ int current_instr_cnt = instr_list.size();
+ int insert_instr_position[];
+ int new_instr_cnt = new_instr.size();
+ insert_instr_position = new[new_instr_cnt];
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(insert_instr_position,
+ foreach(insert_instr_position[i]) {
+ insert_instr_position[i] inside {[0:current_instr_cnt-1]};
+ if(i > 0) {
+ insert_instr_position[i] >= insert_instr_position[i-1];
+ }
+ })
+ if(contained) begin
+ insert_instr_position[0] = 0;
+ if(new_instr_cnt > 1)
+ insert_instr_position[new_instr_cnt-1] = current_instr_cnt-1;
+ end
+ foreach(new_instr[i]) begin
+ insert_instr(new_instr[i], insert_instr_position[i] + i);
+ end
+ endfunction
+
+ function string convert2string();
+ string str;
+ foreach(instr_list[i])
+ str = {str, instr_list[i].convert2asm(), "\n"};
+ return str;
+ endfunction
+
+endclass
+
+// Generate a random instruction stream based on the configuration
+// There are two ways to use this class to generate instruction stream
+// 1. For short instruction stream, you can call randomize() directly.
+// 2. For long instruction stream (>1K), randomize() all instructions together might take a long
+// time for the constraint solver. In this case, you can call gen_instr to generate instructions
+// one by one. The time only grows linearly with the instruction count
+class riscv_rand_instr_stream extends riscv_instr_stream;
+
+ riscv_instr_gen_config cfg;
+ bit access_u_mode_mem = 1'b1;
+ int max_load_store_offset;
+ int max_data_page_id;
+
+ // Some additional reserved registers that should not be used as rd register
+ // by this instruction stream
+ riscv_reg_t reserved_rd[];
+
+ constraint avoid_reserved_rd_c {
+ if(reserved_rd.size() > 0) {
+ foreach(instr_list[i]) {
+ !(instr_list[i].rd inside {reserved_rd});
+ }
+ }
+ }
+
+ `uvm_object_utils(riscv_rand_instr_stream)
+ `uvm_object_new
+
+ virtual function void create_instr_instance();
+ riscv_rand_instr instr;
+ if(cfg == null) begin
+ `uvm_fatal(get_full_name(), "cfg object is null")
+ end
+ for(int i = 0; i < instr_cnt; i++) begin
+ instr = riscv_rand_instr::type_id::create($sformatf("instr_%0d", i));
+ instr.cfg = cfg;
+ instr.reserved_rd = reserved_rd;
+ instr_list.push_back(instr);
+ end
+ endfunction
+
+ function void pre_randomize();
+ if(access_u_mode_mem) begin
+ max_load_store_offset = riscv_instr_pkg::data_page_size;
+ max_data_page_id = riscv_instr_pkg::num_of_data_pages;
+ end else begin
+ max_load_store_offset = riscv_instr_pkg::kernel_data_page_size;
+ max_data_page_id = riscv_instr_pkg::num_of_kernel_data_pages;
+ end
+ endfunction
+
+ virtual function void gen_instr(bit no_branch = 1'b0,
+ bit no_load_store = 1'b1,
+ bit enable_hint_instr = 1'b0);
+ foreach(instr_list[i]) begin
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(instr_list[i],
+ // The last instruction cannot be branch instruction as there's no forward branch target.
+ if((i == instr_list.size() - 1) || no_branch) {
+ category != BRANCH;
+ }
+ if(no_load_store) {
+ !(category inside {LOAD, STORE});
+ })
+ end
+ endfunction
+
+endclass
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
new file mode 100644
index 00000000..5ed069f6
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv
@@ -0,0 +1,330 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// Base class for all load/store instruction stream
+// TODO: Support load/store from instruction section.
+
+class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
+
+ rand int unsigned num_load_store;
+ rand int unsigned num_mixed_instr;
+ rand int base;
+ rand int offset[];
+ rand int addr[];
+ rand int unsigned data_page_id;
+ rand riscv_reg_t rs1_reg;
+ riscv_reg_t reserved_rd[$];
+ // User can specify a small group of available registers to generate various hazard condition
+ rand riscv_reg_t avail_regs[];
+
+ `uvm_object_utils(riscv_load_store_base_instr_stream)
+
+ constraint size_c {
+ offset.size() == num_load_store;
+ addr.size() == num_load_store;
+ }
+
+ constraint rs1_c {
+ !(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
+ }
+
+ constraint addr_c {
+ data_page_id < max_data_page_id;
+ base inside {[0 : max_load_store_offset-1]};
+ foreach(offset[i]) {
+ addr[i] == base + offset[i];
+ // Make sure address is still valid
+ addr[i] inside {[0 : max_load_store_offset - 1]};
+ offset[i] inside {[-2048:2047]};
+ }
+ }
+
+ function new(string name = "");
+ super.new(name);
+ instr_list.rand_mode(0);
+ endfunction
+
+ function void post_randomize();
+ gen_load_store_instr();
+ // rs1 cannot be modified by other instructions
+ if(!(rs1_reg inside {reserved_rd})) begin
+ reserved_rd.push_back(rs1_reg);
+ end
+ add_mixed_instr();
+ add_rs1_init_la_instr();
+ super.post_randomize();
+ endfunction
+
+ // Use "la" instruction to initialize the base regiseter
+ virtual function void add_rs1_init_la_instr();
+ riscv_pseudo_instr la_instr;
+ la_instr = riscv_pseudo_instr::type_id::create("la_instr");
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(la_instr,
+ pseudo_instr_name == LA;
+ rd == rs1_reg;,
+ "Cannot randomize la_instr")
+ if(access_u_mode_mem) begin
+ la_instr.imm_str = $sformatf("data_page_%0d+%0d", data_page_id, base);
+ end else begin
+ la_instr.imm_str = $sformatf("kernel_data_page_%0d+%0d", data_page_id, base);
+ end
+ instr_list.push_front(la_instr);
+ endfunction
+
+ // Generate each load/store instruction
+ virtual function void gen_load_store_instr();
+ riscv_rand_instr rand_instr;
+ riscv_instr_name_t allowed_instr[];
+ if(avail_regs.size() > 0) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(avail_regs,
+ unique{avail_regs};
+ foreach(avail_regs[i]) {
+ !(avail_regs[i] inside {cfg.reserved_regs});
+ },
+ "Cannot randomize avail_regs")
+ end
+ foreach(addr[i]) begin
+ rand_instr = riscv_rand_instr::type_id::create("rand_instr");
+ rand_instr.cfg = cfg;
+ rand_instr.reserved_rd = reserved_rd;
+ // Assign the allowed load/store instructions based on address alignment
+ // This is done separately rather than a constraint to improve the randomization performance
+ allowed_instr = {LB, LBU, SB};
+ if (addr[i][0] == 1'b0) begin
+ allowed_instr = {LH, LHU, SH, allowed_instr};
+ end
+ if (addr[i][1:0] == 2'b00) begin
+ allowed_instr = {LW, SW, LWU, allowed_instr};
+ if((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0)) begin
+ allowed_instr = {C_LW, C_SW, allowed_instr};
+ end
+ end
+ if(addr[i][2:0] == 3'b000) begin
+ allowed_instr = {LD, SD, allowed_instr};
+ if((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0)) begin
+ allowed_instr = {C_LD, C_SD, allowed_instr};
+ end
+ end
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(rand_instr,
+ solve rs1 before rd;
+ rs1 == rs1_reg;
+ instr_name inside {allowed_instr};
+ if(avail_regs.size() > 0) {
+ rd inside {avail_regs};
+ }
+ rd != rs1;
+ )
+ rand_instr.process_load_store = 0;
+ rand_instr.imm_str = $sformatf("%0d", offset[i]);
+ instr_list.push_back(rand_instr);
+ end
+ endfunction
+
+ // Insert some other instructions to mix with load/store instruction
+ virtual function void add_mixed_instr();
+ riscv_rand_instr rand_instr;
+ for(int i = 0; i < num_mixed_instr; i ++) begin
+ rand_instr = riscv_rand_instr::type_id::create("rand_instr");
+ rand_instr.cfg = cfg;
+ rand_instr.reserved_rd = reserved_rd;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(rand_instr,
+ if(avail_regs.size() > 0) {
+ rs1 inside {avail_regs};
+ rd inside {avail_regs};
+ }
+ !(category inside {LOAD, STORE, BRANCH, JUMP});,
+ "Cannot randomize instruction")
+ insert_instr(rand_instr);
+ end
+ endfunction
+
+endclass
+
+// A single load/store instruction
+class riscv_single_load_store_instr_stream extends riscv_load_store_base_instr_stream;
+
+ constraint legal_c {
+ num_load_store == 1;
+ num_mixed_instr < 5;
+ }
+
+ `uvm_object_utils(riscv_load_store_base_instr_stream)
+ `uvm_object_new
+
+endclass
+
+// Back to back load/store instructions
+class riscv_load_store_stress_instr_stream extends riscv_load_store_base_instr_stream;
+
+ int unsigned max_instr_cnt = 30;
+ int unsigned min_instr_cnt = 10;
+
+ constraint legal_c {
+ num_load_store inside {[min_instr_cnt:max_instr_cnt]};
+ num_mixed_instr == 0;
+ }
+
+ `uvm_object_utils(riscv_load_store_stress_instr_stream)
+ `uvm_object_new
+
+endclass
+
+// Random load/store sequence
+// A random mix of load/store instructions and other instructions
+class riscv_load_store_rand_instr_stream extends riscv_load_store_base_instr_stream;
+
+ constraint legal_c {
+ num_load_store inside {[10:30]};
+ num_mixed_instr inside {[10:30]};
+ }
+
+ `uvm_object_utils(riscv_load_store_rand_instr_stream)
+ `uvm_object_new
+
+endclass
+
+// Use a small set of GPR to create various WAW, RAW, WAR hazard scenario
+class riscv_hazard_instr_stream extends riscv_load_store_base_instr_stream;
+
+ int unsigned num_of_avail_regs = 6;
+
+ constraint legal_c {
+ num_load_store inside {[10:30]};
+ num_mixed_instr inside {[10:30]};
+ }
+
+ `uvm_object_utils(riscv_hazard_instr_stream)
+ `uvm_object_new
+
+ function void pre_randomize();
+ avail_regs = new[num_of_avail_regs];
+ super.pre_randomize();
+ endfunction
+
+endclass
+
+// Use a small set of address to create various load/store hazard sequence
+// This instruction stream focus more on hazard handling of load store unit.
+class riscv_load_store_hazard_instr_stream extends riscv_load_store_base_instr_stream;
+
+ rand int avail_addr[];
+
+ constraint legal_c {
+ num_load_store inside {[10:30]};
+ num_mixed_instr inside {[10:30]};
+ }
+
+ constraint avail_addr_c {
+ avail_addr.size() inside {[1:3]};
+ foreach(avail_addr[i]) {
+ avail_addr[i] inside {[0 : max_load_store_offset - 1]};
+ }
+ }
+
+ `uvm_object_utils(riscv_load_store_hazard_instr_stream)
+ `uvm_object_new
+
+ // Randomize each address in the post_randomize to reduce the complexity of solving everything
+ // in one shot.
+ function void post_randomize();
+ int temp_addr;
+ foreach(addr[i]) begin
+ `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(temp_addr,
+ temp_addr inside {avail_addr};,
+ "Cannot randomize address")
+ addr[i] = temp_addr;
+ end
+ endfunction
+
+endclass
+
+// Back-to-back access to the same cache line
+class riscv_cache_line_stress_instr_stream extends riscv_load_store_stress_instr_stream;
+
+ constraint same_cache_line_c {
+ base % riscv_instr_pkg::dcache_line_size_in_bytes == 0;
+ foreach(offset[i]) {
+ offset[i] inside {[0 : riscv_instr_pkg::dcache_line_size_in_bytes-1]};
+ }
+ }
+
+ `uvm_object_utils(riscv_cache_line_stress_instr_stream)
+ `uvm_object_new
+
+endclass
+
+// Back to back access to multiple data pages
+// This is useful to test data TLB switch and replacement
+class riscv_multi_page_load_store_instr_stream extends riscv_directed_instr_stream;
+
+ riscv_load_store_stress_instr_stream load_store_instr_stream[];
+ rand int unsigned num_of_instr_stream;
+ rand int unsigned data_page_id[];
+ rand riscv_reg_t rs1_reg[];
+
+ constraint default_c {
+ foreach(data_page_id[i]) {
+ data_page_id[i] < max_data_page_id;
+ }
+ data_page_id.size() == num_of_instr_stream;
+ rs1_reg.size() == num_of_instr_stream;
+ unique {rs1_reg};
+ unique {data_page_id};
+ num_of_instr_stream inside {[1 : max_data_page_id]};
+ foreach(rs1_reg[i]) {
+ !(rs1_reg[i] inside {cfg.reserved_regs, ZERO});
+ }
+ }
+
+ // Avoid accessing a large number of pages because we may run out of registers for rs1
+ // Each page access needs a reserved register as the base address of load/store instruction
+ constraint reasonable_c {
+ num_of_instr_stream inside {[2:8]};
+ }
+
+ `uvm_object_utils(riscv_multi_page_load_store_instr_stream)
+ `uvm_object_new
+
+ // Generate each load/store seq, and mix them together
+ function void post_randomize();
+ load_store_instr_stream = new[num_of_instr_stream];
+ foreach(load_store_instr_stream[i]) begin
+ load_store_instr_stream[i] = riscv_load_store_stress_instr_stream::type_id::
+ create($sformatf("load_store_instr_stream_%0d", i));
+ load_store_instr_stream[i].min_instr_cnt = 5;
+ load_store_instr_stream[i].max_instr_cnt = 10;
+ load_store_instr_stream[i].cfg = cfg;
+ // Make sure each load/store sequence doesn't override the rs1 of other sequences.
+ foreach(rs1_reg[j]) begin
+ if(i != j) begin
+ load_store_instr_stream[i].reserved_rd.push_back(rs1_reg[j]);
+ end
+ end
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(load_store_instr_stream[i],
+ rs1_reg == local::rs1_reg[i];
+ data_page_id == local::data_page_id[i];,
+ "Cannot randomize load/store instruction")
+ // Mix the instruction stream of different page access, this could trigger the scenario of
+ // frequent data TLB switch
+ if(i == 0) begin
+ instr_list = load_store_instr_stream[i].instr_list;
+ end else begin
+ mix_instr_stream(load_store_instr_stream[i].instr_list);
+ end
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_loop_instr.sv b/vendor/google_riscv-dv/src/riscv_loop_instr.sv
new file mode 100644
index 00000000..b0fe8e1b
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_loop_instr.sv
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// TODO: Support other loop counter update instruction other than ADDI
+// TODO: Support forward branch inside the loop
+
+class riscv_loop_instr extends riscv_rand_instr_stream;
+
+ rand riscv_reg_t loop_cnt_reg[];
+ rand riscv_reg_t loop_limit_reg[];
+ rand int loop_init_val[];
+ rand int loop_step_val[];
+ rand int loop_limit_val[];
+ rand bit [2:0] num_of_nested_loop;
+ rand riscv_instr_name_t branch_type[];
+ rand int num_of_instr_in_loop;
+ riscv_instr_base loop_init_instr[];
+ riscv_instr_base loop_update_instr[];
+ riscv_instr_base loop_branch_instr[];
+ riscv_rand_instr loop_branch_target_instr[];
+ // Aggregated loop instruction stream
+ riscv_instr_base loop_instr[];
+
+ constraint loop_c {
+ solve num_of_nested_loop before loop_cnt_reg;
+ solve num_of_nested_loop before loop_limit_reg;
+ solve num_of_nested_loop before loop_init_val;
+ solve num_of_nested_loop before loop_step_val;
+ solve num_of_nested_loop before loop_limit_val;
+ num_of_instr_in_loop inside {[1:200]};
+ num_of_nested_loop inside {[1:cfg.max_nested_loop]};
+ loop_cnt_reg.size() == num_of_nested_loop;
+ loop_limit_reg.size() == num_of_nested_loop;
+ loop_init_val.size() == num_of_nested_loop;
+ loop_step_val.size() == num_of_nested_loop;
+ loop_limit_val.size() == num_of_nested_loop;
+ branch_type.size() == num_of_nested_loop;
+ foreach(loop_init_val[i]) {
+ loop_cnt_reg[i] inside {cfg.loop_regs};
+ loop_limit_reg[i] inside {cfg.loop_regs};
+ loop_init_val[i] inside {[-10:10]};
+ loop_limit_val[i] inside {[-20:20]};
+ loop_step_val[i] inside {[-10:10]};
+ loop_step_val[i] != 0;
+ if(loop_init_val[i] < loop_limit_val[i]) {
+ loop_step_val[i] > 0;
+ } else {
+ loop_step_val[i] < 0;
+ }
+ // Select a reasonable branch instruction to avoid inifint loop
+ if(loop_init_val[i] < 0 || (loop_limit_val[i] + loop_step_val[i]) < 0) {
+ !(branch_type[i] inside {BLTU, BGEU});
+ }
+ if(((loop_limit_val[i] - loop_init_val[i]) % loop_step_val[i] != 0) ||
+ (loop_limit_val[i] == loop_init_val[i])) {
+ !(branch_type[i] inside {BEQ, BNE});
+ }
+ if(loop_step_val[i] > 0) {
+ branch_type[i] inside {BLTU, BNE, BLT, BEQ};
+ } else {
+ branch_type[i] inside {BGEU, BNE, BGE, BEQ};
+ }
+ }
+ unique {loop_cnt_reg, loop_limit_reg};
+ }
+
+ `uvm_object_utils(riscv_loop_instr)
+ `uvm_object_new
+
+ function void post_randomize();
+ // Generate instructions that mixed with the loop instructions
+ initialize_instr_list(num_of_instr_in_loop);
+ gen_instr(1'b1);
+ // Randomize the key loop instructions
+ loop_init_instr = new[num_of_nested_loop*2];
+ loop_update_instr = new[num_of_nested_loop];
+ loop_branch_instr = new[num_of_nested_loop];
+ loop_branch_target_instr = new[num_of_nested_loop];
+ for(int i = 0; i < num_of_nested_loop; i++) begin
+ // Instruction to init the loop counter
+ loop_init_instr[2*i] = riscv_instr_base::type_id::create("loop_init_instr");
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i],
+ instr_name == ADDI;
+ rd == loop_cnt_reg[i];
+ rs1 == ZERO;
+ imm == loop_init_val[i];,
+ "Cannot randomize loop init insturction")
+ loop_init_instr[2*i].comment = $sformatf("init loop %0d counter", i);
+
+ // Instruction to init loop limit
+ loop_init_instr[2*i+1] = riscv_instr_base::type_id::create("loop_init_instr");
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i+1],
+ instr_name == ADDI;
+ rd == loop_limit_reg[i];
+ rs1 == ZERO;
+ imm == loop_limit_val[i];,
+ "Cannot randomize init loop instruction")
+ loop_init_instr[2*i+1].comment = $sformatf("init loop %0d limit", i);
+
+ // Branch target instruction, can be anything
+ loop_branch_target_instr[i] = riscv_rand_instr::type_id::create("loop_branch_target_instr");
+ loop_branch_target_instr[i].cfg = cfg;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i],
+ !(category inside {LOAD, STORE, BRANCH, JUMP});,
+ "Cannot randomize branch target instruction")
+ loop_branch_target_instr[i].label = $sformatf("%0s_%0d_t", label, i);
+
+ // Instruction to update loop counter
+ loop_update_instr[i] = riscv_instr_base::type_id::create("loop_update_instr");
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_update_instr[i],
+ instr_name == ADDI;
+ rd == loop_cnt_reg[i];
+ rs1== loop_cnt_reg[i];
+ imm == loop_step_val[i];,
+ "Cannot randomize loop update instruction")
+ loop_update_instr[i].comment = $sformatf("update loop %0d counter", i);
+
+ // Backward branch instruction
+ loop_branch_instr[i] = riscv_instr_base::type_id::create("loop_branch_instr");
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_instr[i],
+ instr_name == branch_type[i];
+ rs1 == loop_cnt_reg[i];
+ rs2 == loop_limit_reg[i];,
+ "Cannot randomize backward branch instruction")
+ loop_branch_instr[i].comment = $sformatf("branch for loop %0d", i);
+ loop_branch_instr[i].imm_str = loop_branch_target_instr[i].label;
+ loop_branch_instr[i].branch_assigned = 1'b1;
+ end
+ // Randomly distribute the loop instruction in the existing instruction stream
+ build_loop_instr_stream();
+ mix_instr_stream(loop_instr, 1'b1);
+ foreach(instr_list[i]) begin
+ if(instr_list[i].label != "")
+ instr_list[i].has_label = 1'b1;
+ else
+ instr_list[i].has_label = 1'b0;
+ instr_list[i].atomic = 1'b1;
+ end
+ endfunction
+
+ // Build the whole loop structure from innermost loop to the outermost loop
+ function void build_loop_instr_stream();
+ loop_instr.delete;
+ for(int i = 0; i < num_of_nested_loop; i++) begin
+ loop_instr = {loop_init_instr[2*i],
+ loop_init_instr[2*i+1],
+ loop_branch_target_instr[i],
+ loop_update_instr[i],
+ loop_instr,
+ loop_branch_instr[i]};
+ end
+ `uvm_info(get_full_name(), $sformatf("Totally %0d instructions have been added",
+ loop_instr.size()), UVM_HIGH)
+ endfunction
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_page_table.sv b/vendor/google_riscv-dv/src/riscv_page_table.sv
new file mode 100644
index 00000000..1f5d3002
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_page_table.sv
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// RISC-V page table class
+// This class is defined based on RISC-V privileged spec 1.10, three page table structure is
+// supported: SV32, SV39, SV48
+// This class is used by riscv_page_table_list to generate all page tables the program
+class riscv_page_table#(satp_mode_t MODE = SV39) extends uvm_object;
+
+ int unsigned num_of_pte; // Number of page table entry
+ int unsigned table_id; // Page table ID
+ bit [1:0] level; // Page table level
+ bit [XLEN-1:0] pte_binary[]; // Page table entry in binary format
+ rand riscv_page_table_entry#(MODE) pte[]; // List of all page table entries
+
+ `uvm_object_param_utils(riscv_page_table#(MODE))
+ `uvm_object_new
+
+ // Init page table
+ function void init_page_table(int unsigned num_of_pte = 1);
+ this.num_of_pte = num_of_pte;
+ pte = new[num_of_pte];
+ pte_binary = new[num_of_pte];
+ endfunction
+
+ // Generate the page table binary
+ function void gen_page_table_binary();
+ foreach(pte[i]) begin
+ pte_binary[i] = pte[i].bits;
+ end
+ endfunction
+
+ // Generate the page table section in the output assembly program
+ // Basically it's like a data section with all PTE binaries.
+ function void gen_page_table_section(output string instr[$]);
+ string str;
+ this.gen_page_table_binary();
+ // Align the page table to 4K boundary
+ str = ".align 12";
+ instr.push_back(str);
+ str = $sformatf("%0s:", get_name());
+ instr.push_back(str);
+ foreach(pte_binary[i]) begin
+ if (i % 8 == 0) begin
+ if (XLEN == 64) begin
+ str = $sformatf(".dword 0x%0x", pte_binary[i]);
+ end else begin
+ str = $sformatf(".word 0x%0x", pte_binary[i]);
+ end
+ end else begin
+ str = {str, $sformatf(", 0x%0x", pte_binary[i])};
+ end
+ if (((i + 1) % 8 == 0) || (i == pte_binary.size() - 1)) begin
+ instr.push_back(str);
+ end
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_page_table_entry.sv b/vendor/google_riscv-dv/src/riscv_page_table_entry.sv
new file mode 100644
index 00000000..5027f88f
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_page_table_entry.sv
@@ -0,0 +1,193 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//--------------------------------------------------------------------------------------------
+// RISC-V Page Table Entry(PTE)
+//
+// Support SV32, SV39, SV48 PTE format defined in RISC-V privileged spec 1.10.
+// -------------------------------------------------------------------------------------------
+
+class riscv_page_table_entry#(satp_mode_t MODE = SV39) extends uvm_object;
+
+ // Note that only SV32, SV39, SV48 are supported
+ parameter PPN0_WIDTH = (MODE == SV32) ? 10 : 9;
+ parameter PPN1_WIDTH = (MODE == SV32) ? 12 : 9;
+ parameter PPN2_WIDTH = (MODE == SV39) ? 26 : ((MODE == SV48) ? 9 : 1);
+ parameter PPN3_WIDTH = (MODE == SV48) ? 9 : 1;
+ parameter RSVD_WIDTH = (MODE == SV32) ? 1 : 10;
+ parameter VPN_WIDTH = (MODE == SV32) ? 10 : 9;
+ // Spare bits in virtual address = XLEN - used virtual address bits
+ parameter VADDR_SPARE = (MODE == SV32) ? 0 : (MODE == SV39) ? 25 : 16;
+ // Virtual address bit width
+ parameter VADDR_WIDTH = (MODE == SV32) ? 31 : (MODE == SV39) ? 38 : 48;
+
+ rand bit v; // PTE is valid
+ rand pte_permission_t xwr; // PTE execute-write-read permission
+ rand bit u; // Accessible in User Mode
+ rand bit g; // Gloabal mapping
+ rand bit a; // Accessed flag
+ rand bit d; // Dirty flag
+ rand bit [1:0] rsw; // Reserved for future use
+ rand bit [PPN0_WIDTH-1:0] ppn0;
+ rand bit [PPN1_WIDTH-1:0] ppn1;
+ rand bit [PPN2_WIDTH-1:0] ppn2;
+ rand bit [PPN3_WIDTH-1:0] ppn3;
+ rand bit [XLEN-1:0] bits;
+ rand bit [RSVD_WIDTH-1:0] rsvd;
+ int child_table_id;
+ bit [XLEN-1:0] starting_pa; // Starting physical address
+ bit [XLEN-1:0] starting_va; // Starting virtual address offset
+
+ // This two bits are implementation specific, set them to 1 to avoid mismatching
+ constraint access_dirty_bit_c {
+ soft a == 1'b1;
+ soft d == 1'b1;
+ }
+
+ // Set reserved fields to 0
+ constraint reserved_bits_c {
+ soft rsw == '0;
+ soft rsvd == '0;
+ }
+
+ // PPN is assigned in the post-process
+ constraint ppn_zero_c {
+ soft ppn0 == '0;
+ soft ppn1 == '0;
+ soft ppn2 == '0;
+ soft ppn3 == '0;
+ }
+
+ constraint sw_legal_c {
+ // If the PTE is not a leaf page, U,A,D must be cleared by SW for future compatibility
+ if(xwr == NEXT_LEVEL_PAGE) {
+ u == 1'b0;
+ a == 1'b0;
+ d == 1'b0;
+ }
+ }
+
+ `uvm_object_param_utils(riscv_page_table_entry#(MODE))
+ `uvm_object_new
+
+ virtual function void turn_off_default_constraint();
+ access_dirty_bit_c.constraint_mode(0);
+ reserved_bits_c.constraint_mode(0);
+ ppn_zero_c.constraint_mode(0);
+ sw_legal_c.constraint_mode(0);
+ endfunction
+
+ function void post_randomize();
+ pack_entry();
+ endfunction
+
+ virtual function void do_copy(uvm_object rhs);
+ riscv_page_table_entry#(MODE) rhs_;
+ super.do_copy(rhs);
+ `DV_CHECK_FATAL($cast(rhs_, rhs), "Cast to page_table_entry failed!")
+ this.v = rhs_.v;
+ this.xwr = rhs_.xwr;
+ this.u = rhs_.u;
+ this.g = rhs_.g;
+ this.a = rhs_.a;
+ this.d = rhs_.d;
+ this.rsw = rhs_.rsw;
+ this.ppn0 = rhs_.ppn0;
+ this.ppn1 = rhs_.ppn1;
+ this.ppn2 = rhs_.ppn2;
+ this.ppn3 = rhs_.ppn3;
+ this.bits = rhs_.bits;
+ this.rsvd = rhs_.rsvd;
+ this.starting_pa = rhs_.starting_pa;
+ this.starting_va = rhs_.starting_va;
+ this.child_table_id = rhs_.child_table_id;
+ endfunction
+
+ virtual function string convert2string();
+ string str;
+ str = $sformatf("xwr: %0s, (v)alid:%0d, u: %0d, pa:0x%0x, va:0x%0x",
+ xwr.name(), v, u, starting_pa, starting_va);
+ case(MODE)
+ SV32: str = {str, $sformatf(", ppn[1:0] = %0d/%0d", ppn1, ppn0)};
+ SV39: str = {str, $sformatf(", ppn[2:0] = %0d/%0d/%0d", ppn2, ppn1, ppn0)};
+ SV48: str = {str, $sformatf(", ppn[3:0] = %0d/%0d/%0d/%0d", ppn3, ppn2, ppn1, ppn0)};
+ default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
+ endcase
+ return str;
+ endfunction
+
+ // Pack the PTE to bit stream
+ virtual function void pack_entry();
+ case(MODE)
+ SV32: bits = {ppn1,ppn0,rsw,d,a,g,u,xwr,v};
+ SV39: bits = {rsvd,ppn2,ppn1,ppn0,rsw,d,a,g,u,xwr,v};
+ SV48: bits = {rsvd,ppn3,ppn2,ppn1,ppn0,rsw,d,a,g,u,xwr,v};
+ default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
+ endcase
+ endfunction
+
+ // Return the PPN field offset based on the page level
+ function int get_ppn_offset(bit [1:0] page_level);
+ case(page_level)
+ 0 : return 0;
+ 1 : return PPN0_WIDTH;
+ 2 : return PPN0_WIDTH + PPN1_WIDTH;
+ 3 : return PPN0_WIDTH + PPN1_WIDTH + PPN2_WIDTH;
+ endcase
+ endfunction
+
+ // Assign each PPN field based on the input physical address. This function is used to setup the
+ // leaf PTE to map to the target physical address.
+ // start_pa : Start phyical address.
+ // pte_index : The PTE index of the input page level.
+ // page_level : The page level that this PTE belongs to.
+ function void set_ppn(bit [XLEN-1:0] base_pa, int pte_index, bit[1:0] page_level);
+ int pte_incr[4];
+ int pte_per_table = 4096 / (XLEN/8);
+ ppn0 = base_pa[12 +: PPN0_WIDTH];
+ ppn1 = base_pa[12 + PPN0_WIDTH +: PPN1_WIDTH];
+ if(MODE == SV39) begin
+ ppn2 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH +: PPN2_WIDTH];
+ end else if (MODE == SV48) begin
+ ppn2 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH +: PPN2_WIDTH];
+ ppn3 = base_pa[12 + PPN0_WIDTH + PPN1_WIDTH + PPN2_WIDTH +: PPN3_WIDTH];
+ end
+ foreach(pte_incr[i]) begin
+ if(i >= page_level) begin
+ pte_incr[i] = pte_index % pte_per_table;
+ pte_index = pte_index / pte_per_table;
+ end
+ end
+ ppn0 += pte_incr[0];
+ ppn1 += pte_incr[1];
+ ppn2 += pte_incr[2];
+ ppn3 += pte_incr[3];
+ starting_pa = get_starting_pa();
+ starting_va = starting_pa - base_pa;
+ endfunction
+
+ // Get the starting physical address covered by this PTE
+ function bit[XLEN-1:0] get_starting_pa();
+ case(MODE)
+ SV32: get_starting_pa = {ppn1, ppn0};
+ SV39: get_starting_pa = {ppn2, ppn1, ppn0};
+ SV48: get_starting_pa = {ppn3, ppn2, ppn1, ppn0};
+ default: `uvm_fatal(get_full_name(), $sformatf("Unsupported mode %0x", MODE))
+ endcase
+ get_starting_pa = get_starting_pa << 12;
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_page_table_exception_cfg.sv b/vendor/google_riscv-dv/src/riscv_page_table_exception_cfg.sv
new file mode 100644
index 00000000..dc4b0fe8
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_page_table_exception_cfg.sv
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2018 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.
+ */
+
+class riscv_page_table_exception_cfg extends uvm_object;
+
+ bit enable_exception;
+
+ // Knobs for each type of exception
+ rand bit allow_page_access_control_exception;
+ rand bit allow_superpage_misaligned_exception;
+ rand bit allow_leaf_link_page_exception;
+ rand bit allow_invalid_page_exception;
+ rand bit allow_privileged_mode_exception;
+ rand bit allow_zero_access_bit_exception;
+ rand bit allow_zero_dirty_bit_exception;
+
+ // Exception ratio control
+ int unsigned page_access_fault_ratio = 10;
+ int unsigned misaligned_superpage_ratio = 10;
+ int unsigned leaf_link_page_ratio = 10;
+ int unsigned invalid_page_ratio = 10;
+ int unsigned privl_mode_fault_ratio = 10;
+ int unsigned zero_access_fault_ratio = 5;
+ int unsigned zero_dirty_fault_ratio = 5;
+
+ constraint exception_ratio_c {
+ if(enable_exception) {
+ allow_page_access_control_exception dist { 1 := page_access_fault_ratio,
+ 0 := 100 - page_access_fault_ratio };
+ allow_superpage_misaligned_exception dist { 1 := misaligned_superpage_ratio,
+ 0 := 100 - misaligned_superpage_ratio };
+ allow_leaf_link_page_exception dist { 1 := leaf_link_page_ratio,
+ 0 := 100 - leaf_link_page_ratio };
+ allow_invalid_page_exception dist { 1 := invalid_page_ratio,
+ 0 := 100 - invalid_page_ratio };
+ allow_privileged_mode_exception dist { 1 := privl_mode_fault_ratio,
+ 0 := 100 - privl_mode_fault_ratio };
+ allow_zero_access_bit_exception dist { 1 := zero_access_fault_ratio,
+ 0 := 100 - zero_access_fault_ratio };
+ allow_zero_dirty_bit_exception dist { 1 := zero_dirty_fault_ratio,
+ 0 := 100 - zero_dirty_fault_ratio };
+ } else {
+ allow_page_access_control_exception == 0;
+ allow_superpage_misaligned_exception == 0;
+ allow_leaf_link_page_exception == 0;
+ allow_invalid_page_exception == 0;
+ allow_privileged_mode_exception == 0;
+ allow_zero_access_bit_exception == 0;
+ allow_zero_dirty_bit_exception == 0;
+ }
+ }
+
+ `uvm_object_utils(riscv_page_table_exception_cfg)
+ `uvm_object_new
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_page_table_list.sv b/vendor/google_riscv-dv/src/riscv_page_table_list.sv
new file mode 100644
index 00000000..1de114a1
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_page_table_list.sv
@@ -0,0 +1,510 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//----------------------------------------------------------------------------------------------
+// Complete RISC-V page table generator
+//
+// This class is used to generate all the page tables and link them together.
+// Below features are supported:
+// - Multiple PTEs for each page table
+// - Multiple tables at each level(except for root table)
+// - Mixed leaf entry and non-leaf entry at any level
+// - Allow injecting page table exceptions for any PTE
+//----------------------------------------------------------------------------------------------
+
+class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
+
+ localparam PTE_SIZE = XLEN / 8;
+ localparam PTE_CNT = 4096 / PTE_SIZE;
+ localparam PAGE_LEVEL = (MODE == SV32) ? 2 : ((MODE == SV39) ? 3 : 4);
+ localparam LINK_PTE_PER_TABLE = 2;
+ localparam SUPER_LEAF_PTE_PER_TABLE = 2;
+
+ satp_mode_t mode = MODE;
+
+ // Privileged mode of the program
+ privileged_mode_t privileged_mode = USER_MODE;
+
+ // Starting physical address of the program.
+ bit [XLEN-1:0] start_pa = 'h8000_0000;
+
+ // Num of page table per level
+ int unsigned num_of_page_table[];
+
+ // Page table list, from highest level to the lowest level
+ riscv_page_table#(MODE) page_table[];
+
+ // Root page table PTE idx for the init code entry
+ int unsigned root_init_pte_idx;
+
+ // Instruction generator configuration
+ riscv_instr_gen_config cfg;
+
+ // Allow exception or not
+ bit enable_exception;
+ riscv_page_table_exception_cfg exception_cfg;
+
+ // Valid PTE entry for exception recovery
+ riscv_page_table_entry#(MODE) valid_leaf_pte;
+ riscv_page_table_entry#(MODE) valid_link_pte;
+ riscv_page_table_entry#(MODE) valid_data_leaf_pte;
+ riscv_page_table_entry#(MODE) illegal_pte;
+
+ // Registers used for page table exception handling
+ rand riscv_reg_t level_reg;
+ rand riscv_reg_t fault_vaddr_reg;
+ rand riscv_reg_t pte_addr_reg;
+ rand riscv_reg_t pte_reg;
+ rand riscv_reg_t tmp_reg;
+ rand riscv_reg_t mask_reg;
+ rand riscv_reg_t mpp_reg;
+
+ constraint page_table_exception_handling_reg_c {
+ unique {level_reg, fault_vaddr_reg, pte_addr_reg,
+ pte_reg, tmp_reg, mask_reg, mpp_reg};
+ !(level_reg inside {cfg.reserved_regs, ZERO});
+ !(fault_vaddr_reg inside {cfg.reserved_regs, ZERO});
+ !(pte_addr_reg inside {cfg.reserved_regs, ZERO});
+ !(pte_reg inside {cfg.reserved_regs, ZERO});
+ !(mask_reg inside {cfg.reserved_regs, ZERO});
+ !(mpp_reg inside {cfg.reserved_regs, ZERO});
+ !(tmp_reg inside {cfg.reserved_regs, ZERO});
+ }
+
+ `uvm_object_param_utils(riscv_page_table_list#(MODE))
+
+ function new(string name = "");
+ super.new(name);
+ default_page_table_setting();
+ exception_cfg = riscv_page_table_exception_cfg::type_id::create("exception_cfg");
+ valid_leaf_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_leaf_pte");
+ valid_link_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_link_pte");
+ valid_data_leaf_pte = riscv_page_table_entry#(MODE)::type_id::create("valid_link_pte");
+ illegal_pte = riscv_page_table_entry#(MODE)::type_id::create("illegal_pte");
+ endfunction
+
+ // To avoid large numbers of page tables, by default we limit the number of non-leaf PTE
+ // at higher level. To be more specific, all PTEs of level 0 page table is leaf PTE. For
+ // higher level page table, only PTE[0] and PTE[1] is non-leaf PTE, all other PTEs are leaf
+ // PTE. All leaf PTE should have PPN map to the real physical address of the instruction
+ // or data. For non-leaf PTE, the PPN should map to the physical address of the next PTE.
+ // Take SV39 for example: (PTE_SIZE = 8B)
+ // Table size is 4KB, PTE_SIZE=8B, entry count = 4K/8 = 512
+ // Level 2: Root table, 2 entries, PTE[0] and PTE[1] is non-leaf PTE, PTE[2] is leaf PTE, all
+ // other PTEs are invalid, totally 1 page table with 3 PTEs at this level.
+ // Level 1: Two page tables, map to PTE[0] and PTE[1] of the root table.
+ // Each table has 512 entries, PTE[0], PTE[1] are non-leaf PTE, cover 4MB memory
+ // space. PTE[2:511] are leaf PTE, cover 510 * 2MB memory space.
+ // Level 0: 4 page tables at this level(map to PTE[0] and PTE[1] of the previous level),
+ // each table has 512 leaf PTE.
+ // In summary, 7(1+2+4) tables are needed for SV39.
+ // Similarly, 3 (1+2) page tables for SV32, 15 (1 + 2 + 4 + 8) page tables for SV48.
+ // Note:
+ // - The number of randomization call is optmized to improve performance
+ // - PPN assignment is done at program run time
+ virtual function void randomize_page_table();
+ int pte_index;
+ exception_cfg.enable_exception = enable_exception;
+ create_valid_pte();
+ foreach(page_table[i]) begin
+ `uvm_info(`gfn, $sformatf("Randomizing page table %0d, num of PTE: %0d",
+ i, page_table[i].pte.size()), UVM_LOW)
+ if(i == 0) begin
+ pte_index = 0;
+ end else if(page_table[i].level != page_table[i-1].level) begin
+ pte_index = 0;
+ end
+ foreach(page_table[i].pte[j]) begin
+ if(page_table[i].level > 0) begin
+ // Superpage
+ if (j < LINK_PTE_PER_TABLE) begin
+ // First few super pages are link PTE to the next level
+ $cast(page_table[i].pte[j], valid_link_pte.clone());
+ end else if (j < SUPER_LEAF_PTE_PER_TABLE + LINK_PTE_PER_TABLE) begin
+ // Non-link superpage table entry
+ $cast(page_table[i].pte[j], valid_leaf_pte.clone());
+ end else begin
+ // Invalid unused PTEs
+ page_table[i].pte[j] = riscv_page_table_entry#(MODE)::type_id::
+ create($sformatf("pte_%0d_%0d",i, j));
+ page_table[i].pte[j].v = 1'b0;
+ end
+ end else begin
+ // Lowest level leaf pages
+ $cast(page_table[i].pte[j], valid_leaf_pte.clone());
+ end
+ if(page_table[i].pte[j].xwr != NEXT_LEVEL_PAGE) begin
+ page_table[i].pte[j].set_ppn(start_pa, pte_index, page_table[i].level);
+ end
+ pte_index++;
+ if(enable_exception) begin
+ inject_page_table_exception(page_table[i].pte[j], page_table[i].level);
+ end
+ page_table[i].pte[j].pack_entry();
+ `uvm_info(`gfn, $sformatf("%0s PT_%0d_%0d: %0s", privileged_mode.name(),
+ i, j, page_table[i].pte[j].convert2string()), UVM_HIGH)
+ end
+ end
+ endfunction
+
+ // Create the basic legal page table entries
+ virtual function void create_valid_pte();
+ // Randomize a valid leaf PTE entry
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(valid_leaf_pte,
+ // Set the correct privileged mode
+ if(privileged_mode == USER_MODE) {
+ u == 1'b1;
+ } else {
+ // Accessing user mode page from supervisor mode is only allowed when MSTATUS.SUM and
+ // MSTATUS.MPRV are both 1
+ if(!(cfg.mstatus_sum && cfg.mstatus_mprv)) {
+ u == 1'b0;
+ }
+ }
+ // Set a,d bit to 1 avoid page/access fault exceptions
+ a == 1'b1;
+ d == 1'b1;
+ // Default: Readable, writable, executable page
+ soft xwr == R_W_EXECUTE_PAGE;
+ // Page is valid
+ v == 1'b1;
+ )
+ $cast(valid_link_pte, valid_leaf_pte.clone());
+ $cast(valid_data_leaf_pte, valid_leaf_pte.clone());
+ illegal_pte.turn_off_default_constraint();
+ valid_link_pte.xwr = NEXT_LEVEL_PAGE;
+ valid_link_pte.pack_entry();
+ // Set data page to read/write, but not executable
+ valid_data_leaf_pte.xwr = READ_WRITE_PAGE;
+ valid_data_leaf_pte.pack_entry();
+ endfunction
+
+ virtual function void inject_page_table_exception(riscv_page_table_entry#(MODE) pte, int level);
+ `DV_CHECK_RANDOMIZE_FATAL(exception_cfg)
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_pte,
+ !(xwr inside {NEXT_LEVEL_PAGE, R_W_EXECUTE_PAGE});)
+ // Wrong privielge mode setting
+ if(exception_cfg.allow_privileged_mode_exception) begin
+ pte.u = ~pte.u;
+ end
+ // Random access control
+ // The link PTE is unchanged to avoid changing page table mappings
+ if(exception_cfg.allow_page_access_control_exception &&
+ (pte.xwr != NEXT_LEVEL_PAGE)) begin
+ pte.xwr = illegal_pte.xwr;
+ end
+ // Invalid page exception
+ if(exception_cfg.allow_invalid_page_exception) begin
+ pte.v = 0;
+ end
+ // Set "access" bit to zero
+ if(exception_cfg.allow_zero_access_bit_exception) begin
+ pte.a = 0;
+ end
+ // Set "dirty" bit to zero
+ if(exception_cfg.allow_zero_dirty_bit_exception) begin
+ pte.d = 0;
+ end
+ // Unaligned super leaf PTE
+ if(exception_cfg.allow_superpage_misaligned_exception &&
+ (level > 0) && (pte.xwr != NEXT_LEVEL_PAGE)) begin
+ bit [riscv_page_table_entry#(MODE)::VPN_WIDTH-1:0] fault_ppn;
+ `DV_CHECK_STD_RANDOMIZE_FATAL(fault_ppn)
+ if(level == 3) begin
+ pte.ppn2 = fault_ppn;
+ end else if (level == 2) begin
+ pte.ppn1 = fault_ppn;
+ end else begin
+ pte.ppn0 = fault_ppn;
+ end
+ end
+ // Illegal link PTE for the lowest level page table
+ if(exception_cfg.allow_leaf_link_page_exception && (level == 0)) begin
+ pte.xwr = NEXT_LEVEL_PAGE;
+ end
+ endfunction
+
+ // Page fault handling routine
+ // There are two types of page fault handling routine.
+ // 1. For page table error injection test, fix all PTE related to the virtual address by walking
+ // through the page table with the fault address.
+ // 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.
+ virtual function void gen_page_fault_handling_routine(output string instr[$]);
+ int unsigned level;
+ string load_store_unit;
+ bit[XLEN-1:0] bit_mask = '1;
+
+ if(MODE == SV48) begin
+ load_store_unit = "d";
+ level = 3;
+ bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::RSVD_WIDTH +
+ riscv_page_table_entry#(MODE)::PPN3_WIDTH);
+ end else if(MODE == SV39) begin
+ load_store_unit = "d";
+ level = 2;
+ bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::RSVD_WIDTH +
+ riscv_page_table_entry#(MODE)::PPN2_WIDTH);
+ end else if(MODE == SV32) begin
+ load_store_unit = "w";
+ level = 1;
+ bit_mask = bit_mask >> (riscv_page_table_entry#(MODE)::PPN1_WIDTH);
+ end else begin
+ `uvm_fatal(`gfn, "Unsupported MODE")
+ end
+
+ if(cfg.mstatus_mprv && (SATP_MODE != BARE)) begin
+ // Check if mstatus.mpp equals to machine mode(0x11)
+ // If MPP != Machine_mode and MSTATUS.MPRV = 1, load/store address translation is the same as
+ // the mode indicated by MPP
+ instr.push_back($sformatf("csrr x%0d, 0x%0x // MSTATUS", mpp_reg, MSTATUS));
+ instr.push_back($sformatf("srli x%0d, x%0d, 11", mpp_reg, mpp_reg));
+ instr.push_back($sformatf("andi x%0d, x%0d, 0x3", mpp_reg, mpp_reg));
+ instr.push_back($sformatf("xori x%0d, x%0d, 0x3", mpp_reg, mpp_reg));
+ end
+
+ // Flush TLB to force synchronization
+ instr.push_back("sfence.vma x0, x0");
+
+ // Start from root level, top-down fix all related PTEs
+ instr.push_back($sformatf("li x%0d, %0d", level_reg, level));
+ instr.push_back($sformatf("li x%0d, 0x%0x", mask_reg, bit_mask));
+ // Get the address that causes the page fault
+ instr.push_back($sformatf("csrr x%0d, 0x%0x # MTVAL", fault_vaddr_reg, MTVAL));
+ // Remove lower 4KB offset
+ instr.push_back($sformatf("srli x%0d, x%0d, 12", fault_vaddr_reg, fault_vaddr_reg));
+ // Remove the virtual address spare bits, align the VPN to the msb
+ instr.push_back($sformatf("slli x%0d, x%0d, %0d", fault_vaddr_reg, fault_vaddr_reg,
+ riscv_page_table_entry#(MODE)::VADDR_SPARE + 12));
+
+ // Starting from the root table
+ instr.push_back($sformatf("la x%0d, page_table_0", pte_addr_reg));
+
+ instr.push_back("fix_pte:");
+ // Get the VPN of the current level
+ // Note the VPN under process is on the msb, right shift XLEN - VPN_WIDTH to get the VPN value
+ instr.push_back($sformatf("srli x%0d, x%0d, %0d",
+ tmp_reg, fault_vaddr_reg,
+ XLEN - riscv_page_table_entry#(MODE)::VPN_WIDTH));
+ // Get the actual address offset within the page table
+ instr.push_back($sformatf("slli x%0d, x%0d, %0d",
+ tmp_reg, tmp_reg, $clog2(XLEN/8)));
+ // Add page table starting address and PTE offset to get PTE physical address
+ instr.push_back($sformatf("add x%0d, x%0d, x%0d",
+ pte_addr_reg, pte_addr_reg, tmp_reg));
+ // Load the PTE from the memory
+ instr.push_back($sformatf("l%0s x%0d, 0(x%0d)",
+ load_store_unit, pte_reg, pte_addr_reg));
+ // Check if the it's a link PTE (PTE[4:1] == 0)
+ instr.push_back($sformatf("slli x%0d, x%0d, %0d",
+ tmp_reg, pte_reg, XLEN - 4));
+ instr.push_back($sformatf("srli x%0d, x%0d, %0d",
+ tmp_reg, tmp_reg, XLEN - 3));
+ instr.push_back($sformatf("bne zero, x%0d, fix_leaf_pte", tmp_reg));
+
+ // Handle link PTE exceptions
+ // - If level == 0, change the link PTE to leaf PTE, and finish exception handling
+ instr.push_back($sformatf("beq zero, x%0d, fix_leaf_pte", level_reg));
+ // - If level != 0, fix the link PTE, and move to the PTE it points to
+ // - Override the low 10 bits with the correct link PTE setting
+ instr.push_back($sformatf("srli x%0d, x%0d, 10", pte_reg, pte_reg));
+ instr.push_back($sformatf("slli x%0d, x%0d, 10", pte_reg, pte_reg));
+ instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, valid_link_pte.bits));
+ instr.push_back($sformatf("or x%0d, x%0d, x%0d", pte_reg, pte_reg, tmp_reg));
+ instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
+ // - Zero out 10 lower access control bits
+ instr.push_back($sformatf("srli x%0d, x%0d, 10", pte_addr_reg, pte_reg));
+ // - Left shift 12 bits to create the physical address
+ instr.push_back($sformatf("slli x%0d, x%0d, 12", pte_addr_reg, pte_addr_reg));
+ // - Remove the VPN of the current level
+ instr.push_back($sformatf("slli x%0d, x%0d, %0d", fault_vaddr_reg, fault_vaddr_reg,
+ riscv_page_table_entry#(MODE)::VPN_WIDTH));
+ // - Decrement the level, update the bit mask
+ instr.push_back($sformatf("addi x%0d, x%0d, -1", level_reg, level_reg));
+ instr.push_back($sformatf("srli x%0d, x%0d, %0d",
+ mask_reg, mask_reg, riscv_page_table_entry#(MODE)::VPN_WIDTH));
+ // - Jump to fix the PTE of the next level
+ instr.push_back("j fix_pte");
+
+ // fix_leaf_pte: Override the low 10 bits with the correct leaf PTE setting
+ instr.push_back("fix_leaf_pte:");
+ // Use mask to zero out lower 10 bits and unaligned VPN
+ instr.push_back($sformatf("not x%0d, x%0d", mask_reg, mask_reg));
+ instr.push_back($sformatf("and x%0d, x%0d, x%0d", pte_reg, pte_reg, mask_reg));
+ instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, valid_leaf_pte.bits));
+ instr.push_back($sformatf("or x%0d, x%0d, x%0d", pte_reg, pte_reg, tmp_reg));
+ instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
+ instr.push_back("j fix_kernel_leaf_pte");
+
+ // Fix kernel leaf PTE
+ instr.push_back("fix_kernel_leaf_pte:");
+ // - Load the starting virtual address of the kernel space
+ instr.push_back($sformatf("la x%0d, _kernel_start", tmp_reg));
+ instr.push_back($sformatf("slli x%0d, x%0d, %0d", tmp_reg, tmp_reg,
+ XLEN - MAX_USED_VADDR_BITS));
+ instr.push_back($sformatf("srli x%0d, x%0d, %0d", tmp_reg, tmp_reg,
+ XLEN - MAX_USED_VADDR_BITS));
+ instr.push_back($sformatf("csrr x%0d, 0x%0x # MTVAL", fault_vaddr_reg, MTVAL));
+ // - Check if the fault virtual address is in the kernel space
+ instr.push_back($sformatf("bgeu x%0d, x%0d, fix_pte_done", tmp_reg, fault_vaddr_reg));
+ // - Set the PTE.u bit to 0 for kernel space PTE
+ instr.push_back($sformatf("li x%0d, 0x%0x", tmp_reg, 'h10));
+ instr.push_back($sformatf("not x%0d, x%0d", tmp_reg, tmp_reg));
+ instr.push_back($sformatf("and x%0d, x%0d, x%0d", pte_reg, tmp_reg, pte_reg));
+ instr.push_back($sformatf("s%0s x%0d, 0(x%0d)", load_store_unit, pte_reg, pte_addr_reg));
+
+ // End of page table fault handling
+ instr.push_back("fix_pte_done:");
+ // Randomly decide if run some kernel program before exiting from exception handling
+ // Use the low 2 bits of x30 to determine whether to skip it or not.
+ instr.push_back($sformatf("slli x30, x30, %0d", XLEN - 2));
+ instr.push_back("beqz x30, fix_pte_ret");
+ // Randomly decide if set MPRV to 1
+ instr.push_back($sformatf("slli x31, x31, %0d", XLEN - 2));
+ instr.push_back("beqz x30, check_mprv");
+ instr.push_back($sformatf("csrr x%0d, 0x%0x", tmp_reg, MSTATUS));
+ instr.push_back($sformatf("li x%0d, 0x%0x", mask_reg, MPRV_BIT_MASK));
+ instr.push_back($sformatf("not x%0d, x%0d", mask_reg, mask_reg));
+ instr.push_back($sformatf("or x%0d, x%0d, 0x%0x", tmp_reg, tmp_reg, mask_reg));
+ instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", tmp_reg, MSTATUS, tmp_reg));
+ // Run some kernel mode program before returning from exception handling
+ // If MPRV = 0, jump to regular kernel mode program
+ // If MPRV = 1, jump to kernel program with U mode mem load/store
+ instr.push_back($sformatf("check_mprv: li x%0d, 0x%0x", mask_reg, MPRV_BIT_MASK));
+ instr.push_back($sformatf("csrr x%0d, 0x%0x", tmp_reg, MSTATUS));
+ instr.push_back($sformatf("and x%0d, x%0d, x%0d", tmp_reg, tmp_reg, mask_reg));
+ instr.push_back($sformatf("beqz x%0d, j_smode", tmp_reg));
+ instr.push_back("jal ra, smode_ls_umem_program");
+ instr.push_back("j fix_pte_ret");
+ 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);
+ instr.push_back("mret");
+
+ foreach(instr[i]) begin
+ instr[i] = instr[i].tolower();
+ end
+
+ endfunction
+
+ virtual function void default_page_table_setting();
+ num_of_page_table = new[PAGE_LEVEL];
+ foreach(num_of_page_table[i]) begin
+ num_of_page_table[i] = LINK_PTE_PER_TABLE ** (PAGE_LEVEL - i - 1);
+ end
+ endfunction
+
+ virtual function void create_page_table_list();
+ page_table = new[num_of_page_table.sum()];
+ foreach(page_table[i]) begin
+ page_table[i] = riscv_page_table#(MODE)::type_id::create($sformatf("page_table_%0d",i));
+ page_table[i].init_page_table(PTE_CNT);
+ page_table[i].table_id = i;
+ page_table[i].level = get_level(i);
+ end
+ endfunction
+
+ virtual function int get_1st_4k_table_id();
+ foreach(page_table[i]) begin
+ if(page_table[i].level == 0) return i;
+ end
+ return -1;
+ endfunction
+
+ // Link page table
+ virtual function void process_page_table(output string instr[$]);
+ string load_store_unit;
+ int pte_addr_offset;
+ bit [XLEN-1:0] ubit_mask = '1;
+ ubit_mask[4] = 1'b0; // U bit of PTE
+ load_store_unit = (XLEN == 32) ? "w" : "d";
+ // 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)};
+ foreach(page_table[i].pte[j]) begin
+ if(j >= SUPER_LEAF_PTE_PER_TABLE) continue;
+ pte_addr_offset = (j * PTE_SIZE) - 2048;
+ `uvm_info(`gfn, $sformatf("Processing PT_%0d_PTE_%0d, v = %0d, level = %0d",
+ 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
+ instr = {instr,
+ // Load the current PTE value
+ $sformatf("l%0s x22, %0d(x21)", load_store_unit, pte_addr_offset),
+ // Load the target page table physical address, PPN should be 0
+ $sformatf("la x20, page_table_%0d # Link PT_%0d_PTE_%0d -> PT_%0d",
+ 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"),
+ // Assign PPN
+ "or x22, x20, x22",
+ // Store the new PTE value
+ $sformatf("s%0s x22, %0d(x21)", load_store_unit, pte_addr_offset)};
+ end
+ end
+ end
+ // ---------------------------------------
+ // Set the kernel page u bit to 0
+ // ---------------------------------------
+ // Load the start and end address of the kernel space
+ if (cfg.support_supervisor_mode) begin
+ instr = {instr,
+ "la x20, _kernel_start",
+ "la x21, _kernel_end",
+ // 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)),
+ // 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),
+ "process_kernel_pte:",
+ // Load the PTE from the memory
+ $sformatf("l%0s x23, 0(x20)", load_store_unit),
+ // Unset U bit
+ "and x23, x23, x22",
+ // Save PTE back to memory
+ $sformatf("l%0s x23, 0(x20)", load_store_unit),
+ // Move to the next PTE
+ $sformatf("addi x20, x20, %0d", XLEN/8),
+ // If not the end of the kernel space, process the next PTE
+ "ble x20, x21, process_kernel_pte"};
+ end
+ endfunction
+
+ // If you want to create custom page table topology, override the below tasks to specify the
+ // level and parent of each table.
+ virtual function int get_level(int table_id);
+ for(int level = PAGE_LEVEL - 1; level >= 0; level--) begin
+ if(table_id < num_of_page_table[level]) return level;
+ table_id -= num_of_page_table[level];
+ end
+ endfunction
+
+ virtual function int get_child_table_id(int table_id, int pte_id);
+ return table_id * LINK_PTE_PER_TABLE + pte_id + 1;
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_privil_reg.sv b/vendor/google_riscv-dv/src/riscv_privil_reg.sv
new file mode 100644
index 00000000..4ae206ad
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_privil_reg.sv
@@ -0,0 +1,575 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// RISC-V privileged register class
+class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
+
+ `uvm_object_utils(riscv_privil_reg)
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ function void init_reg(REG_T reg_name);
+ super.init_reg(reg_name);
+ case(reg_name) inside
+ /////////////// Machine mode reigster //////////////
+ // Machine ISA Register
+ MISA: begin
+ privil_level = M_LEVEL;
+ add_field("WARL0", 26, WARL);
+ add_field("WIRI", XLEN-28, WIRI);
+ add_field("MXL", 2, WARL);
+ end
+ // Machine Vendor ID Register
+ MVENDORID: begin
+ privil_level = M_LEVEL;
+ add_field("OFFSET", 7, WIRI);
+ add_field("BANK", XLEN-7, WIRI);
+ end
+ // Machine Architecture ID Register
+ MARCHID: begin
+ privil_level = M_LEVEL;
+ add_field("ARCHITECTURE_ID", XLEN, WIRI);
+ end
+ // Machine Implementation ID Register
+ MIMPID: begin
+ privil_level = M_LEVEL;
+ add_field("IMPLEMENTATION", XLEN, WIRI);
+ end
+ // Hart ID Register
+ MHARTID: begin
+ privil_level = M_LEVEL;
+ add_field("HART_ID", XLEN, WIRI);
+ end
+ // Machine Status Register
+ MSTATUS: begin
+ privil_level = M_LEVEL;
+ add_field("UIE", 1, WARL);
+ add_field("SIE", 1, WARL);
+ add_field("WPRI0", 1, WPRI);
+ add_field("MIE", 1, WARL);
+ add_field("UPIE", 1, WARL);
+ add_field("SPIE", 1, WARL);
+ add_field("WPRI1", 1, WPRI);
+ add_field("MPIE", 1, WARL);
+ add_field("SPP", 1, WLRL);
+ add_field("WPRI2", 2, WPRI);
+ add_field("MPP", 2, WLRL);
+ add_field("FS", 2, WARL);
+ add_field("XS", 2, WARL);
+ add_field("MPRV", 1, WARL);
+ add_field("SUM", 1, WARL);
+ add_field("MXR", 1, WARL);
+ add_field("TVM", 1, WARL);
+ add_field("TW", 1, WARL);
+ add_field("TSR", 1, WARL);
+ if(XLEN == 32) begin
+ add_field("WPRI3", 8, WPRI);
+ end else begin
+ add_field("WPRI3", 9, WPRI);
+ add_field("UXL", 2, WARL);
+ add_field("SXL", 2, WARL);
+ add_field("WPRI4", XLEN - 37, WPRI);
+ end
+ add_field("SD", 1, WARL);
+ end
+ // Machine Trap-Vector Base-Address Register
+ MTVEC: begin
+ privil_level = M_LEVEL;
+ add_field("MODE", 2, WARL);
+ add_field("BASE", XLEN - 2, WARL);
+ end
+ // Machine Exception Delegation Register
+ MEDELEG: begin
+ privil_level = M_LEVEL;
+ add_field("IAM", 1, WARL);
+ add_field("IAF", 1, WARL);
+ add_field("ILGL", 1, WARL);
+ add_field("BREAK", 1, WARL);
+ add_field("LAM", 1, WARL);
+ add_field("LAF", 1, WARL);
+ add_field("SAM", 1, WARL);
+ add_field("SAF", 1, WARL);
+ add_field("ECFU", 1, WARL);
+ add_field("ECFS", 1, WARL);
+ add_field("WARL0", 1, WARL);
+ add_field("ECFM", 1, WARL);
+ add_field("IPF", 1, WARL);
+ add_field("LPF", 1, WARL);
+ add_field("WARL1", 1, WARL);
+ add_field("SPF", 1, WARL);
+ add_field("WARL2", XLEN-16, WARL);
+ end
+ // Machine Interrupt Delegation Register
+ MIDELEG: begin
+ privil_level = M_LEVEL;
+ add_field("USIP", 1, WARL);
+ add_field("SSIP", 1, WARL);
+ add_field("WARL0", 1, WARL);
+ add_field("MSIP", 1, WARL);
+ add_field("UTIP", 1, WARL);
+ add_field("STIP", 1, WARL);
+ add_field("WARL1", 1, WARL);
+ add_field("MTIP", 1, WARL);
+ add_field("UEIP", 1, WARL);
+ add_field("SEIP", 1, WARL);
+ add_field("WARL2", 1, WARL);
+ add_field("MEIP", 1, WARL);
+ add_field("WARL3", XLEN-12, WARL);
+ end
+ // Machine trap-enable register
+ MIP: begin
+ privil_level = M_LEVEL;
+ add_field("USIP", 1, WARL);
+ add_field("SSIP", 1, WARL);
+ add_field("WIRI0", 1, WIRI);
+ add_field("MSIP", 1, WARL);
+ add_field("UTIP", 1, WARL);
+ add_field("STIP", 1, WARL);
+ add_field("WPRI1", 1, WPRI);
+ add_field("MTIP", 1, WARL);
+ add_field("UEIP", 1, WARL);
+ add_field("SEIP", 1, WARL);
+ add_field("WIRI2", 1, WIRI);
+ add_field("MEIP", 1, WARL);
+ add_field("WIRI3", XLEN - 12, WIRI);
+ end
+ // Machine interrupt-enable register
+ MIE: begin
+ privil_level = M_LEVEL;
+ add_field("USIE", 1, WARL);
+ add_field("SSIE", 1, WARL);
+ add_field("WPRI0", 1, WPRI);
+ add_field("MSIE", 1, WARL);
+ add_field("UTIE", 1, WARL);
+ add_field("STIE", 1, WARL);
+ add_field("WPRI1", 1, WPRI);
+ add_field("MTIE", 1, WARL);
+ add_field("UEIE", 1, WARL);
+ add_field("SEIE", 1, WARL);
+ add_field("WPRI2", 1, WPRI);
+ add_field("MEIE", 1, WARL);
+ add_field("WPRI3", XLEN - 12, WPRI);
+ end
+ // Cycle Count Register
+ MCYCLE: begin
+ privil_level = M_LEVEL;
+ add_field("MCYCLE", 64, WIRI);
+ end
+ // Instruction Count Register
+ MINSTRET: begin
+ privil_level = M_LEVEL;
+ add_field("MINSTRET", 64, WIRI);
+ end
+ // Cycle Count Register - RV32I only
+ MCYCLEH: begin
+ privil_level = M_LEVEL;
+ add_field("MCYCLEH", 32, WIRI);
+ end
+ // Instruction Count Register - RV32I only
+ MINSTRETH: begin
+ privil_level = M_LEVEL;
+ add_field("MINSTRETH", 32, WIRI);
+ end
+ // Hardware Performance Monitor Counters
+ [MHPMCOUNTER3:MHPMCOUNTER31]: begin
+ privil_level = M_LEVEL;
+ add_field($sformatf("%s", reg_name.name()), XLEN, WIRI);
+ end
+ // Hardware Performance Monitor Events
+ [MHPMEVENT3:MHPMEVENT31]: begin
+ privil_level = M_LEVEL;
+ add_field($sformatf("%s", reg_name.name()), XLEN, WARL);
+ end
+ // Hardware Performance Monitor Counters - RV32I only
+ [MHPMCOUNTER3H:MHPMCOUNTER31H]: begin
+ if(XLEN != 32) begin
+ `uvm_fatal(get_full_name(), $sformatf("Register %s is only in RV32I", reg_name.name()))
+ end
+ privil_level = M_LEVEL;
+ add_field($sformatf("%s", reg_name.name()), 32, WIRI);
+ end
+ // Machine Counter Enable Register
+ MCOUNTEREN: begin
+ privil_level = M_LEVEL;
+ add_field("CY", 1, WARL);
+ add_field("TM", 1, WARL);
+ add_field("IR", 1, WARL);
+ add_field("HPM3", 1, WARL);
+ add_field("HPM4", 1, WARL);
+ add_field("HPM5", 1, WARL);
+ add_field("HPM6", 1, WARL);
+ add_field("HPM7", 1, WARL);
+ add_field("HPM8", 1, WARL);
+ add_field("HPM9", 1, WARL);
+ add_field("HPM10", 1, WARL);
+ add_field("HPM11", 1, WARL);
+ add_field("HPM12", 1, WARL);
+ add_field("HPM13", 1, WARL);
+ add_field("HPM14", 1, WARL);
+ add_field("HPM15", 1, WARL);
+ add_field("HPM16", 1, WARL);
+ add_field("HPM17", 1, WARL);
+ add_field("HPM18", 1, WARL);
+ add_field("HPM19", 1, WARL);
+ add_field("HPM20", 1, WARL);
+ add_field("HPM21", 1, WARL);
+ add_field("HPM22", 1, WARL);
+ add_field("HPM23", 1, WARL);
+ add_field("HPM24", 1, WARL);
+ add_field("HPM25", 1, WARL);
+ add_field("HPM26", 1, WARL);
+ add_field("HPM27", 1, WARL);
+ add_field("HPM28", 1, WARL);
+ add_field("HPM29", 1, WARL);
+ add_field("HPM30", 1, WARL);
+ add_field("HPM31", 1, WARL);
+ if(XLEN == 64) begin
+ add_field("WPRI", 32, WPRI);
+ end
+ end
+ // Machine Scratch Register
+ MSCRATCH: begin
+ privil_level = M_LEVEL;
+ add_field("MSCRATCH", XLEN, WARL);
+ end
+ // Machine Exception Program Counter
+ MEPC: begin
+ privil_level = M_LEVEL;
+ add_field("BASE", XLEN, WARL);
+ end
+ // Machine Cause Register
+ MCAUSE: begin
+ privil_level = M_LEVEL;
+ add_field("CODE", 4, WLRL);
+ add_field("WLRL", XLEN-5, WLRL);
+ add_field("INTERRUPT", 1, WARL);
+ end
+ // Machine Trap Value
+ MTVAL: begin
+ privil_level = M_LEVEL;
+ add_field("VALUE", XLEN, WARL);
+ end
+ // Physical Memory Protection Configuration Register
+ PMPCFG0: begin
+ privil_level = M_LEVEL;
+ add_field("PMP0CFG", 8, WARL);
+ add_field("PMP1CFG", 8, WARL);
+ add_field("PMP2CFG", 8, WARL);
+ add_field("PMP3CFG", 8, WARL);
+ if(XLEN==64) begin
+ add_field("PMP4CFG", 8, WARL);
+ add_field("PMP5CFG", 8, WARL);
+ add_field("PMP6CFG", 8, WARL);
+ add_field("PMP7CFG", 8, WARL);
+ end
+ end
+ // Physical Memory Protection Configuration Register
+ PMPCFG1: begin
+ privil_level = M_LEVEL;
+ if(XLEN==64) begin
+ add_field("PMP8CFG", 8, WARL);
+ add_field("PMP9CFG", 8, WARL);
+ add_field("PMP10CFG", 8, WARL);
+ add_field("PMP11CFG", 8, WARL);
+ add_field("PMP12CFG", 8, WARL);
+ add_field("PMP13CFG", 8, WARL);
+ add_field("PMP14CFG", 8, WARL);
+ add_field("PMP15CFG", 8, WARL);
+ end else begin
+ add_field("PMP4CFG", 8, WARL);
+ add_field("PMP5CFG", 8, WARL);
+ add_field("PMP6CFG", 8, WARL);
+ add_field("PMP7CFG", 8, WARL);
+ end
+ end
+ // Physical Memory Protection Configuration Register
+ PMPCFG2: begin
+ if(XLEN!=32) begin
+ `uvm_fatal(get_full_name(), "CSR PMPCFG2 only exists in RV32.")
+ end
+ privil_level = M_LEVEL;
+ add_field("PMP8CFG", 8, WARL);
+ add_field("PMP9CFG", 8, WARL);
+ add_field("PMP10CFG", 8, WARL);
+ add_field("PMP11CFG", 8, WARL);
+ end
+ // Physical Memory Protection Configuration Register
+ PMPCFG3: begin
+ if(XLEN!=32) begin
+ `uvm_fatal(get_full_name(), "CSR PMPCFG3 only exists in RV32.")
+ end
+ privil_level = M_LEVEL;
+ add_field("PMP12CFG", 8, WARL);
+ add_field("PMP13CFG", 8, WARL);
+ add_field("PMP14CFG", 8, WARL);
+ add_field("PMP15CFG", 8, WARL);
+ end
+ // Physical Memory Protection Configuration Registers
+ [PMPADDR0:PMPADDR15]: begin
+ privil_level = M_LEVEL;
+ if(XLEN==64) begin
+ add_field("ADDRESS", 54, WARL);
+ add_field("WIRI", 10, WIRI);
+ end else begin
+ add_field("ADDRESS", 32, WARL);
+ end
+ end
+ /////////////// Supervisor mode reigster //////////////
+ // Supervisor status register
+ SSTATUS: begin
+ privil_level = S_LEVEL;
+ add_field("UIE", 1, WARL);
+ add_field("SIE", 1, WARL);
+ add_field("WPRI0", 2, WPRI);
+ add_field("UPIE", 1, WARL);
+ add_field("SPIE", 1, WARL);
+ add_field("WPRI1", 2, WPRI);
+ add_field("SPP", 1, WLRL);
+ add_field("WPRI2", 4, WPRI);
+ add_field("FS", 2, WARL);
+ add_field("XS", 2, WARL);
+ add_field("WPRI3", 1, WPRI);
+ add_field("SUM", 1, WARL);
+ add_field("MXR", 1, WARL);
+ if(XLEN == 32) begin
+ add_field("WPRI4", 11, WPRI);
+ end else begin
+ add_field("WPRI4", 12, WPRI);
+ add_field("UXL", 2, WARL);
+ add_field("WPRI4", XLEN - 35, WPRI);
+ end
+ add_field("SD", 1, WARL);
+ end
+ // Supervisor Trap Vector Base Address Register
+ STVEC: begin
+ privil_level = S_LEVEL;
+ add_field("MODE", 2, WARL);
+ add_field("BASE", XLEN-2, WLRL);
+ end
+ // Supervisor Exception Delegation Register
+ SEDELEG: begin
+ privil_level = S_LEVEL;
+ add_field("IAM", 1, WARL);
+ add_field("IAF", 1, WARL);
+ add_field("II", 1, WARL);
+ add_field("WPRI0", 1, WPRI);
+ add_field("LAM", 1, WARL);
+ add_field("LAF", 1, WARL);
+ add_field("SAM", 1, WARL);
+ add_field("SAF", 1, WARL);
+ add_field("ECFU", 1, WARL);
+ add_field("WPRI1", 1, WPRI);
+ add_field("WARL0", 1, WARL);
+ add_field("WPRI2", 1, WPRI);
+ add_field("IPF", 1, WARL);
+ add_field("LPF", 1, WARL);
+ add_field("WARL1", 1, WARL);
+ add_field("SPF", 1, WARL);
+ add_field("WARL2", XLEN-16, WARL);
+ end
+ // Supervisor Interrupt Delegation Register
+ SIDELEG: begin
+ privil_level = S_LEVEL;
+ add_field("USIP", 1, WARL);
+ add_field("SSIP", 1, WARL);
+ add_field("WARL0", 1, WARL);
+ add_field("WPRI0", 1, WPRI);
+ add_field("UTIP", 1, WARL);
+ add_field("STIP", 1, WARL);
+ add_field("WARL1", 1, WARL);
+ add_field("WPRI1", 1, WPRI);
+ add_field("UEIP", 1, WARL);
+ add_field("SEIP", 1, WARL);
+ add_field("WARL2", 1, WARL);
+ add_field("WPRI2", 1, WPRI);
+ add_field("WARL3", XLEN-12, WARL);
+ end
+ // Supervisor trap-enable register
+ SIP: begin
+ privil_level = S_LEVEL;
+ add_field("USIP", 1, WARL);
+ add_field("SSIP", 1, WARL);
+ add_field("WPRI0", 2, WPRI);
+ add_field("UTIP", 1, WARL);
+ add_field("STIP", 1, WARL);
+ add_field("WPRI1", 2, WPRI);
+ add_field("UEIP", 1, WARL);
+ add_field("SEIP", 1, WARL);
+ add_field("WPRI2", 2, WPRI);
+ add_field("WPRI3", XLEN - 12, WPRI);
+ end
+ // Supervisor interrupt-enable register
+ SIE: begin
+ privil_level = S_LEVEL;
+ add_field("USIE", 1, WARL);
+ add_field("SSIE", 1, WARL);
+ add_field("WPRI0", 2, WPRI);
+ add_field("UTIE", 1, WARL);
+ add_field("STIE", 1, WARL);
+ add_field("WPRI1", 2, WPRI);
+ add_field("UEIE", 1, WARL);
+ add_field("SEIE", 1, WARL);
+ add_field("WPRI2", XLEN - 10, WPRI);
+ end
+ // Supervisor Counter Enable Register
+ SCOUNTEREN: begin
+ privil_level = S_LEVEL;
+ add_field("CY", 1, WARL);
+ add_field("TM", 1, WARL);
+ add_field("IR", 1, WARL);
+ add_field("HPM3", 1, WARL);
+ add_field("HPM4", 1, WARL);
+ add_field("HPM5", 1, WARL);
+ add_field("HPM6", 1, WARL);
+ add_field("HPM7", 1, WARL);
+ add_field("HPM8", 1, WARL);
+ add_field("HPM9", 1, WARL);
+ add_field("HPM10", 1, WARL);
+ add_field("HPM11", 1, WARL);
+ add_field("HPM12", 1, WARL);
+ add_field("HPM13", 1, WARL);
+ add_field("HPM14", 1, WARL);
+ add_field("HPM15", 1, WARL);
+ add_field("HPM16", 1, WARL);
+ add_field("HPM17", 1, WARL);
+ add_field("HPM18", 1, WARL);
+ add_field("HPM19", 1, WARL);
+ add_field("HPM20", 1, WARL);
+ add_field("HPM21", 1, WARL);
+ add_field("HPM22", 1, WARL);
+ add_field("HPM23", 1, WARL);
+ add_field("HPM24", 1, WARL);
+ add_field("HPM25", 1, WARL);
+ add_field("HPM26", 1, WARL);
+ add_field("HPM27", 1, WARL);
+ add_field("HPM28", 1, WARL);
+ add_field("HPM29", 1, WARL);
+ add_field("HPM30", 1, WARL);
+ add_field("HPM31", 1, WARL);
+ if(XLEN == 64) begin
+ add_field("WPRI", 32, WPRI);
+ end
+ end
+ // Supervisor Scratch Register
+ SSCRATCH: begin
+ privil_level = S_LEVEL;
+ add_field("SSCRATCH", XLEN, WARL);
+ end
+ // Supervisor Exception Program Counter
+ SEPC: begin
+ privil_level = S_LEVEL;
+ add_field("BASE", XLEN, WARL);
+ end
+ // Supervisor Cause Register
+ SCAUSE: begin
+ privil_level = S_LEVEL;
+ add_field("CODE", 4, WLRL);
+ add_field("WLRL", XLEN-5, WLRL);
+ add_field("INTERRUPT", 1, WARL);
+ end
+ // Supervisor Trap Value
+ STVAL: begin
+ privil_level = S_LEVEL;
+ add_field("VALUE", XLEN, WARL);
+ end
+ // Supervisor Address Translation and Protection
+ SATP: begin
+ privil_level = S_LEVEL;
+ if(XLEN == 32) begin
+ add_field("PPN", 22, WARL);
+ add_field("ASID", 9, WARL);
+ add_field("MODE", 1, WARL);
+ end else begin
+ add_field("PPN", 44, WARL);
+ add_field("ASID", 16, WARL);
+ add_field("MODE", 4, WARL);
+ end
+ end
+ /////////////// User mode reigster //////////////
+ // User Status Register
+ USTATUS: begin
+ privil_level = U_LEVEL;
+ add_field("UIE", 1, WARL);
+ add_field("WPRI0", 3, WPRI);
+ add_field("UPIE", 1, WARL);
+ add_field("WPRI1", XLEN-5, WPRI);
+ end
+ // User Trap Vector Base Address Register
+ UTVEC: begin
+ privil_level = U_LEVEL;
+ add_field("MODE", 2, WARL);
+ add_field("BASE", XLEN-2, WLRL);
+ end
+ // User Interrupt-Enable register
+ UIE: begin
+ privil_level = U_LEVEL;
+ add_field("USIE", 1, WARL);
+ add_field("WPRI0", 3, WPRI);
+ add_field("UTIE", 1, WARL);
+ add_field("WPRI1", 3, WPRI);
+ add_field("UEIE", 1, WARL);
+ add_field("WPRI2", XLEN-9, WPRI);
+ end
+ // User Trap-Enable register
+ UIP: begin
+ privil_level = U_LEVEL;
+ add_field("USIP", 1, WARL);
+ add_field("WPRI0", 3, WPRI);
+ add_field("UTIP", 1, WARL);
+ add_field("WPRI1", 3, WPRI);
+ add_field("UEIP", 1, WARL);
+ add_field("WPRI2", XLEN-9, WPRI);
+ end
+ // User Scratch Register
+ USCRATCH: begin
+ privil_level = U_LEVEL;
+ add_field("MSCRATCH", XLEN, WARL);
+ end
+ // User Exception Program Counter
+ UEPC: begin
+ privil_level = U_LEVEL;
+ add_field("BASE", XLEN, WARL);
+ end
+ // User Cause Register
+ UCAUSE: begin
+ privil_level = U_LEVEL;
+ add_field("CODE", 4, WLRL);
+ add_field("WLRL", XLEN-5, WLRL);
+ add_field("INTERRUPT", 1, WARL);
+ end
+ // User Trap Value
+ UTVAL: begin
+ privil_level = U_LEVEL;
+ add_field("VALUE", XLEN, WARL);
+ end
+ default:
+ `uvm_fatal(get_full_name(), $sformatf("reg %0s is not supported yet", reg_name.name()))
+ endcase
+ set_wiri_wpri_fields();
+ endfunction
+
+ // Hardwire all WIRI and WPRI fields to '0
+ virtual function void set_wiri_wpri_fields();
+ foreach(fld[i]) begin
+ if(fld[i].access_type inside {WIRI, WPRI}) begin
+ set_field(fld[i].get_name(), '0, 1'b1);
+ end
+ end
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv b/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv
new file mode 100644
index 00000000..7e038e91
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_privileged_common_seq.sv
@@ -0,0 +1,169 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// This class provides some common routines for privileged mode operations
+class riscv_privileged_common_seq extends uvm_sequence;
+
+ riscv_instr_gen_config cfg;
+ riscv_privil_reg mstatus;
+ riscv_privil_reg mie;
+ riscv_privil_reg sstatus;
+ riscv_privil_reg sie;
+ riscv_privil_reg ustatus;
+ riscv_privil_reg uie;
+
+ `uvm_object_utils(riscv_privileged_common_seq)
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ virtual function void enter_privileged_mode(input privileged_mode_t mode,
+ output string instrs[$]);
+ string label = format_string({"init_", mode.name(), ":"}, LABEL_STR_LEN);
+ string ret_instr[] = {"mret"};
+ riscv_privil_reg regs[$];
+ label = label.tolower();
+ setup_mmode_reg(mode, regs);
+ if(mode != MACHINE_MODE) begin
+ setup_smode_reg(mode, regs);
+ setup_satp(instrs);
+ ret_instr.shuffle();
+ end
+ gen_csr_instr(regs, instrs);
+ // Use mret/sret to switch to the target privileged mode
+ instrs.push_back(ret_instr[0]);
+ foreach(instrs[i]) begin
+ instrs[i] = {indent, instrs[i]};
+ end
+ instrs.push_front(label);
+ endfunction
+
+ virtual function void setup_mmode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
+ mstatus = riscv_privil_reg::type_id::create("mstatus");
+ mie = riscv_privil_reg::type_id::create("mie");
+ mstatus.init_reg(MSTATUS);
+ mie.init_reg(MIE);
+ `DV_CHECK_RANDOMIZE_FATAL(mstatus, "cannot randomize mstatus");
+ if(XLEN==64) begin
+ mstatus.set_field("UXL", 2'b10);
+ mstatus.set_field("SXL", 2'b10);
+ end
+ mstatus.set_field("XS", 0);
+ mstatus.set_field("FS", 0);
+ mstatus.set_field("SD", 0);
+ mstatus.set_field("UIE", 0);
+ mstatus.set_field("MPRV", cfg.mstatus_mprv);
+ mstatus.set_field("MXR", cfg.mstatus_mxr);
+ mstatus.set_field("SUM", cfg.mstatus_sum);
+ mstatus.set_field("TVM", cfg.mstatus_tvm);
+ // Set the previous privileged mode as the target mode
+ mstatus.set_field("MPP", mode);
+ if(mode == USER_MODE)
+ mstatus.set_field("SPP", 0);
+ else
+ mstatus.set_field("SPP", 1);
+ // Enable interrupt
+ mstatus.set_field("MPIE", cfg.enable_interrupt);
+ mstatus.set_field("MIE", cfg.enable_interrupt);
+ mstatus.set_field("SPIE", cfg.enable_interrupt);
+ mstatus.set_field("SIE", cfg.enable_interrupt);
+ mstatus.set_field("UPIE", cfg.enable_interrupt);
+ mstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap);
+ regs.push_back(mstatus);
+ // Enable external and timer interrupt
+ mie.set_field("UEIE", cfg.enable_interrupt);
+ mie.set_field("SEIE", cfg.enable_interrupt);
+ mie.set_field("MEIE", cfg.enable_interrupt);
+ mie.set_field("USIE", cfg.enable_interrupt);
+ mie.set_field("SSIE", cfg.enable_interrupt);
+ mie.set_field("MSIE", cfg.enable_interrupt);
+ regs.push_back(mie);
+ endfunction
+
+ virtual function void setup_smode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
+ sstatus = riscv_privil_reg::type_id::create("sstatus");
+ sie = riscv_privil_reg::type_id::create("sie");
+ sstatus.init_reg(SSTATUS);
+ sie.init_reg(SIE);
+ `DV_CHECK_RANDOMIZE_FATAL(sstatus, "cannot randomize sstatus")
+ sstatus.set_field("SPIE", cfg.enable_interrupt);
+ sstatus.set_field("SIE", cfg.enable_interrupt);
+ sstatus.set_field("UPIE", cfg.enable_interrupt);
+ sstatus.set_field("UIE", riscv_instr_pkg::support_umode_trap);
+ if(XLEN==64) begin
+ sstatus.set_field("UXL", 2'b10);
+ end
+ sstatus.set_field("XS", 0);
+ sstatus.set_field("FS", 0);
+ sstatus.set_field("SD", 0);
+ sstatus.set_field("UIE", 0);
+ if(mode == USER_MODE)
+ sstatus.set_field("SPP", 0);
+ else
+ sstatus.set_field("SPP", 1);
+ regs.push_back(sstatus);
+ // Enable external and timer interrupt
+ sie.set_field("UEIE", cfg.enable_interrupt);
+ sie.set_field("SEIE", cfg.enable_interrupt);
+ sie.set_field("USIE", cfg.enable_interrupt);
+ sie.set_field("SSIE", cfg.enable_interrupt);
+ regs.push_back(sie);
+ endfunction
+
+ virtual function void setup_umode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
+ ustatus = riscv_privil_reg::type_id::create("ustatus");
+ uie = riscv_privil_reg::type_id::create("uie");
+ ustatus.init_reg(USTATUS);
+ uie.init_reg(UIE);
+ `DV_CHECK_RANDOMIZE_FATAL(ustatus, "cannot randomize ustatus")
+ ustatus.set_field("UIE", cfg.enable_interrupt);
+ ustatus.set_field("UPIE", cfg.enable_interrupt);
+ regs.push_back(ustatus);
+ uie.set_field("UEIE", cfg.enable_interrupt);
+ uie.set_field("USIE", cfg.enable_interrupt);
+ regs.push_back(uie);
+ endfunction
+
+ 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()));
+ end
+ endfunction
+
+ virtual function void setup_satp(ref string instrs[$]);
+ riscv_privil_reg satp;
+ bit [XLEN-1:0] satp_ppn_mask;
+ if(SATP_MODE == BARE) return;
+ 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));
+ 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");
+ // 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");
+ // Set the PPN field for SATP
+ instrs.push_back($sformatf("csrs 0x%0x, a0 // satp", SATP));
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_rand_instr.sv b/vendor/google_riscv-dv/src/riscv_rand_instr.sv
new file mode 100644
index 00000000..8799ead2
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_rand_instr.sv
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2018 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.
+ */
+
+class riscv_rand_instr extends riscv_instr_base;
+
+ riscv_instr_gen_config cfg;
+
+ // Some additional reserved registers
+ riscv_reg_t reserved_rd[];
+
+ `uvm_object_utils(riscv_rand_instr)
+
+ constraint instr_c {
+ solve instr_name before imm;
+ solve instr_name before rs1;
+ solve instr_name before rs2;
+ !(instr_name inside {riscv_instr_pkg::unsupported_instr});
+ category inside {LOAD, STORE, SHIFT, ARITHMETIC, LOGICAL, BRANCH, COMPARE, CSR, SYSTEM, SYNCH};
+ group inside {riscv_instr_pkg::supported_isa};
+ // Avoid using any special purpose register as rd, those registers are reserved for
+ // special instructions
+ !(rd inside {cfg.reserved_regs});
+ if(reserved_rd.size() > 0) {
+ !(rd inside {reserved_rd});
+ }
+ // Compressed instruction may use the same CSR for both rs1 and rd
+ if(group inside {RV32C, RV64C, RV128C, RV32FC, RV32DC}) {
+ !(rs1 inside {cfg.reserved_regs, reserved_rd});
+ }
+ // Below instrutions will modify stack pointer, not allowed in normal instruction stream.
+ // It can be used in stack operation instruction stream.
+ !(instr_name inside {C_SWSP, C_SDSP, C_ADDI16SP});
+ // Avoid using reserved registers as rs1 (base address)
+ if(category inside {LOAD, STORE}) {
+ !(rs1 inside {reserved_rd, cfg.reserved_regs, ZERO});
+ }
+ if(!cfg.enable_sfence) {
+ instr_name != SFENCE_VMA;
+ }
+ if(cfg.no_fence) {
+ !(instr_name inside {FENCE, FENCEI, SFENCE_VMA});
+ }
+ // TODO: Support C_ADDI4SPN
+ instr_name != C_ADDI4SPN;
+ }
+
+ constraint rvc_csr_c {
+ // Registers specified by the three-bit rs1’, rs2’, and rd’ fields of the CIW, CL, CS,
+ // and CB formats
+ if(format inside {CIW_FORMAT, CL_FORMAT, CS_FORMAT, CB_FORMAT}) {
+ rs1 inside {[S0:A5]};
+ rs2 inside {[S0:A5]};
+ rd inside {[S0:A5]};
+ }
+ // C_ADDI16SP is only valid when rd == SP
+ if(instr_name == C_ADDI16SP) {
+ rd == SP;
+ }
+ }
+
+ constraint constraint_cfg_knob_c {
+ if(cfg.no_csr_instr == 1) {
+ category != CSR;
+ }
+ if(cfg.no_ebreak) {
+ instr_name != EBREAK;
+ instr_name != C_EBREAK;
+ }
+ // Below previleged instruction is not generated by default
+ !(instr_name inside {ECALL, WFI, URET, SRET, MRET});
+ if(cfg.no_load_store) {
+ category != LOAD;
+ category != STORE;
+ }
+ if(cfg.no_branch_jump) {
+ category != BRANCH;
+ }
+ }
+
+ `uvm_object_new
+
+endclass
diff --git a/vendor/google_riscv-dv/src/riscv_reg.sv b/vendor/google_riscv-dv/src/riscv_reg.sv
new file mode 100644
index 00000000..63d6e09f
--- /dev/null
+++ b/vendor/google_riscv-dv/src/riscv_reg.sv
@@ -0,0 +1,159 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// --------------------------------------------------
+// Light weight RISC-V register class library
+// --------------------------------------------------
+
+// Base class for RISC-V register field
+class riscv_reg_field extends uvm_object;
+
+ int unsigned bit_width;
+ bit [XLEN-1:0] reset_val;
+ rand bit [XLEN-1:0] val;
+ reg_field_access_t access_type;
+ bit hard_wired;
+
+ constraint zero_reserved_field_c {
+ (access_type == WPRI) -> (val == '0);
+ }
+
+ constraint hardwired_fld_c {
+ (hard_wired) -> (val == reset_val);
+ }
+
+ `uvm_object_utils(riscv_reg_field)
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ function string convert2string();
+ return($sformatf("%0s bit_width:%0d val:0x%0x type:%0s",
+ get_name(), bit_width, val, access_type));
+ endfunction
+
+ function void post_randomize();
+ bit [XLEN-1:0] mask = '1;
+ mask = mask >> (XLEN-bit_width);
+ val = mask & val;
+ endfunction
+
+endclass
+
+// Base class for RISC-V register
+class riscv_reg#(type REG_T = privileged_reg_t) extends uvm_object;
+
+ REG_T reg_name;
+ riscv_csr_t offset;
+ privileged_level_t privil_level;
+ bit [XLEN-1:0] val;
+ rand riscv_reg_field fld[$];
+
+ `uvm_object_param_utils(riscv_reg#(REG_T))
+
+ function new(string name = "");
+ super.new(name);
+ endfunction
+
+ virtual function void init_reg(REG_T reg_name);
+ this.reg_name = reg_name;
+ offset = riscv_csr_t'(reg_name);
+ endfunction
+
+ virtual function bit[XLEN-1:0] get_val();
+ int total_len;
+ total_len = fld.sum() with (item.bit_width);
+ if(total_len != XLEN) begin
+ foreach(fld[i])
+ $display(fld[i].convert2string());
+ `uvm_fatal(get_full_name(),
+ $sformatf("Total field length %0d != XLEN %0d", total_len, XLEN))
+ end
+ val = '0;
+ foreach(fld[i]) begin
+ val = (val << fld[i].bit_width) | fld[i].val;
+ end
+ return val;
+ endfunction
+
+ virtual function void add_field(string fld_name, int unsigned bit_width,
+ reg_field_access_t access_type,
+ bit [XLEN-1:0] reset_val = '0);
+ riscv_reg_field new_fld;
+ new_fld = riscv_reg_field::type_id::create(fld_name);
+ new_fld.bit_width = bit_width;
+ new_fld.access_type = access_type;
+ new_fld.reset_val = reset_val;
+ fld.push_front(new_fld);
+ endfunction
+
+ virtual function void set_field(string fld_name, bit[XLEN-1:0] val, bit hard_wired = 1'b0);
+ foreach(fld[i]) begin
+ if(fld_name == fld[i].get_name()) begin
+ fld[i].val = val;
+ fld[i].hard_wired = hard_wired;
+ if(hard_wired) begin
+ fld[i].reset_val = val;
+ end
+ return;
+ end
+ end
+ `uvm_fatal(get_full_name(), $sformatf("Cannot match found field %0s", fld_name))
+ endfunction
+
+ virtual function riscv_reg_field get_field_by_name(string fld_name);
+ foreach(fld[i]) begin
+ if(fld_name == fld[i].get_name()) begin
+ return fld[i];
+ end
+ end
+ `uvm_fatal(get_full_name(), $sformatf("Cannot match found field %0s", fld_name))
+ endfunction
+
+ virtual function void rand_field(string fld_name);
+ riscv_reg_field fld_hd = get_field_by_name(fld_name);
+ `DV_CHECK_RANDOMIZE_FATAL(fld_hd)
+ endfunction
+
+ virtual function void set_field_rand_mode(string fld_name, bit rand_on);
+ riscv_reg_field fld_hd = get_field_by_name(fld_name);
+ fld_hd.rand_mode(rand_on);
+ endfunction
+
+ virtual function void reset();
+ foreach(fld[i]) begin
+ fld[i].val = fld[i].reset_val;
+ end
+ endfunction
+
+ virtual function void set_val(bit [XLEN-1:0] val);
+ foreach(fld[i]) begin
+ if(!fld[i].hard_wired) begin
+ // Assign the valid msb to the field
+ fld[i].val = (val >> (XLEN - fld[i].bit_width));
+ `uvm_info(get_full_name(), $sformatf(
+ "Assign field %0s, bit_width:%0d, reg_val 0x%0x, fld_val:0x%0x",
+ fld[i].get_name(), fld[i].bit_width, val, fld[i].val), UVM_LOW)
+ end
+ val = val << fld[i].bit_width;
+ end
+ endfunction
+
+ virtual function void set_wiri_wpri_fields();
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/test/riscv_instr_base_test.sv b/vendor/google_riscv-dv/test/riscv_instr_base_test.sv
new file mode 100644
index 00000000..502da421
--- /dev/null
+++ b/vendor/google_riscv-dv/test/riscv_instr_base_test.sv
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2018 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.
+ */
+
+// Base test
+class riscv_instr_base_test extends uvm_test;
+
+ riscv_instr_gen_config cfg;
+ string test_opts;
+ string asm_file_name = "riscv_asm_test";
+ riscv_asm_program_gen asm_gen;
+ string instr_seq;
+
+ `uvm_component_utils(riscv_instr_base_test)
+
+ function new(string name="", uvm_component parent=null);
+ super.new(name, parent);
+ void'($value$plusargs("asm_file_name=%0s", asm_file_name));
+ endfunction
+
+ virtual function void build_phase(uvm_phase phase);
+ super.build_phase(phase);
+ cfg = riscv_instr_gen_config::type_id::create("cfg");
+ uvm_config_db#(riscv_instr_gen_config)::set(null, "*", "instr_cfg", cfg);
+ if(cfg.asm_test_suffix != "")
+ asm_file_name = {asm_file_name, ".", cfg.asm_test_suffix};
+ // Override the default riscv instruction sequence
+ if($value$plusargs("instr_seq=%0s", instr_seq)) begin
+ uvm_coreservice_t coreservice = uvm_coreservice_t::get();
+ uvm_factory factory = coreservice.get_factory();
+ factory.set_type_override_by_name("riscv_instr_sequence", instr_seq);
+ end
+ endfunction
+
+ function void report_phase(uvm_phase phase);
+ uvm_report_server rs;
+ int error_count;
+
+ rs = uvm_report_server::get_server();
+
+ error_count = rs.get_severity_count(UVM_WARNING) +
+ rs.get_severity_count(UVM_ERROR) +
+ rs.get_severity_count(UVM_FATAL);
+
+ if (error_count == 0) begin
+ `uvm_info("", "TEST PASSED", UVM_NONE);
+ end else begin
+ `uvm_info("", "TEST FAILED", UVM_NONE);
+ end
+ super.report_phase(phase);
+ endfunction
+
+ function void get_directed_instr_stream_opts();
+ string instr_name;
+ int ratio;
+ string cmd_opts_prefix;
+ int i = 0;
+ while(1) begin
+ cmd_opts_prefix = $sformatf("directed_instr_%0d", i);
+ if($value$plusargs({cmd_opts_prefix, "=%0s"}, instr_name) &&
+ $value$plusargs({cmd_opts_prefix, "_ratio=%0d"}, ratio)) begin
+ asm_gen.add_directed_instr_stream(instr_name, ratio);
+ end else begin
+ break;
+ end
+ `uvm_info(`gfn, $sformatf("Got directed instr[%0d] %0s, ratio = %0d/1000",
+ i, instr_name, ratio), UVM_LOW)
+ i++;
+ end
+
+ endfunction
+
+ virtual function void apply_directed_instr();
+ endfunction
+
+ task run_phase(uvm_phase phase);
+ int fd;
+ for(int i = 0; i < cfg.num_of_tests; i++) begin
+ string test_name;
+ cfg = riscv_instr_gen_config::type_id::create("cfg");
+ randomize_cfg();
+ asm_gen = riscv_asm_program_gen::type_id::create("asm_gen");
+ get_directed_instr_stream_opts();
+ asm_gen.cfg = cfg;
+ test_name = $sformatf("%0s.%0d.S", asm_file_name, i);
+ apply_directed_instr();
+ `uvm_info(`gfn, "All directed instruction is applied", UVM_LOW)
+ asm_gen.gen_program();
+ asm_gen.gen_test_file(test_name);
+ end
+ endtask
+
+ virtual function void randomize_cfg();
+ `DV_CHECK_RANDOMIZE_FATAL(cfg);
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/test/riscv_instr_gen_tb_top.sv b/vendor/google_riscv-dv/test/riscv_instr_gen_tb_top.sv
new file mode 100644
index 00000000..b889d01a
--- /dev/null
+++ b/vendor/google_riscv-dv/test/riscv_instr_gen_tb_top.sv
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2018 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.
+ */
+
+module riscv_instr_gen_tb_top;
+
+ `include "uvm_macros.svh"
+
+ import uvm_pkg::*;
+ import riscv_instr_test_pkg::*;
+
+ initial begin
+ run_test();
+ end
+
+endmodule
diff --git a/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv b/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv
new file mode 100644
index 00000000..04c4e722
--- /dev/null
+++ b/vendor/google_riscv-dv/test/riscv_instr_test_lib.sv
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2018 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.
+ */
+
+//================================================================================================
+// RISC-V Test Library
+//------------------------------------------------------------------------------------------------
+// riscv_arithmetic_basic_test :
+// - Simple arithmetic instruction test, [machine mode]
+//
+// riscv_machine_mode_rand_test :
+// - Random instruction test, include sub-programs, branch, load/store [machine mode]
+//
+// riscv_privileged_mode_rand_test :
+// - Similar to riscv_machine_mode_rand_test, run in supervisor mode or user mode if supported
+//
+// riscv_rand_instr_test :
+// - A full random test, with a mix of directed instruction stream
+//
+// riscv_rand_jump_test :
+// - Random jump among a large number of sub programs
+//
+// riscv_mmu_stress_test:
+// - A mixed of intense load/store instructions
+//
+// riscv_page_table_exception_test:
+// - Running test in privileged mode with page table exceptions
+//
+// riscv_no_fence_test
+// - Random instruction with fence disabled, allow more intense instruction execution
+//
+// riscv_sfence_exception_test
+// - Random instruction with sfence exception. sfence.vma could be excuted in non-supervisor mode
+// or mstatus.tvm is set.
+//
+// riscv_illegal_instr_test:
+// - Random instruction mixed with illegal instructions
+//
+// riscv_hint_instr_test:
+// - Random instruction mixed with HINT instructions
+//
+// riscv_privileged_csr_test:
+// - To be released soon
+//
+//================================================================================================
+
+class riscv_arithmetic_basic_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_arithmetic_basic_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 10000;
+ cfg.num_of_sub_program = 0;
+ cfg.no_fence = 1;
+ cfg.no_data_page = 1'b1;
+ cfg.no_branch_jump = 1'b1;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ init_privileged_mode == MACHINE_MODE;
+ max_nested_loop == 0;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+endclass
+
+class riscv_machine_mode_rand_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_machine_mode_rand_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 10000;
+ cfg.num_of_sub_program = 5;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ init_privileged_mode == MACHINE_MODE;
+ max_nested_loop == 0;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+ virtual function void apply_directed_instr();
+ // Add load/store instruction stream
+ asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
+ endfunction
+
+endclass
+
+class riscv_privileged_mode_rand_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_privileged_mode_rand_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 10000;
+ cfg.num_of_sub_program = 5;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ init_privileged_mode != MACHINE_MODE;
+ max_nested_loop == 0;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+ virtual function void apply_directed_instr();
+ // Add load/store instruction stream
+ asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
+ endfunction
+
+endclass
+
+class riscv_rand_instr_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_rand_instr_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 10000;
+ cfg.num_of_sub_program = 5;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ max_nested_loop == 2;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+ virtual function void apply_directed_instr();
+ // Mix below directed instructino streams with the random instructions
+ asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 4);
+ asm_gen.add_directed_instr_stream("riscv_loop_instr", 4);
+ asm_gen.add_directed_instr_stream("riscv_hazard_instr_stream", 4);
+ asm_gen.add_directed_instr_stream("riscv_load_store_hazard_instr_stream", 4);
+ asm_gen.add_directed_instr_stream("riscv_cache_line_stress_instr_stream", 4);
+ asm_gen.add_directed_instr_stream("riscv_multi_page_load_store_instr_stream", 4);
+ endfunction
+
+endclass
+
+class riscv_rand_jump_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_rand_jump_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 20000;
+ cfg.num_of_sub_program = 20;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ max_nested_loop == 1;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+ virtual function void apply_directed_instr();
+ asm_gen.add_directed_instr_stream("riscv_load_store_rand_instr_stream", 10);
+ endfunction
+
+endclass
+
+class riscv_mmu_stress_test extends riscv_instr_base_test;
+
+ `uvm_component_utils(riscv_mmu_stress_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.instr_cnt = 500;
+ cfg.num_of_sub_program = 0;
+ `DV_CHECK_RANDOMIZE_WITH_FATAL(cfg,
+ max_nested_loop == 0;)
+ `uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
+ cfg.sprint()), UVM_LOW)
+ endfunction
+
+ virtual function void apply_directed_instr();
+ // Mix below directed instructino streams with the random instructions
+ asm_gen.add_directed_instr_stream("riscv_cache_line_stress_instr_stream", 80);
+ asm_gen.add_directed_instr_stream("riscv_load_store_hazard_instr_stream", 80);
+ asm_gen.add_directed_instr_stream("riscv_multi_page_load_store_instr_stream", 80);
+ endfunction
+
+endclass
+
+class riscv_page_table_exception_test extends riscv_rand_instr_test;
+
+ `uvm_component_utils(riscv_page_table_exception_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.enable_page_table_exception = 1'b1;
+ super.randomize_cfg();
+ endfunction
+
+endclass
+
+class riscv_no_fence_test extends riscv_rand_instr_test;
+
+ `uvm_component_utils(riscv_no_fence_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.no_fence = 1'b1;
+ super.randomize_cfg();
+ endfunction
+
+endclass
+
+class riscv_sfence_exception_test extends riscv_rand_instr_test;
+
+ `uvm_component_utils(riscv_sfence_exception_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.allow_sfence_exception = 1'b1;
+ super.randomize_cfg();
+ endfunction
+
+endclass
+
+class riscv_illegal_instr_test extends riscv_rand_instr_test;
+
+ `uvm_component_utils(riscv_illegal_instr_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.enable_illegal_instruction = 1'b1;
+ super.randomize_cfg();
+ endfunction
+
+endclass
+
+class riscv_hint_instr_test extends riscv_rand_instr_test;
+
+ `uvm_component_utils(riscv_hint_instr_test)
+ `uvm_component_new
+
+ virtual function void randomize_cfg();
+ cfg.enable_hint_instruction = 1'b1;
+ super.randomize_cfg();
+ endfunction
+
+endclass
diff --git a/vendor/google_riscv-dv/test/riscv_instr_test_pkg.sv b/vendor/google_riscv-dv/test/riscv_instr_test_pkg.sv
new file mode 100644
index 00000000..a8a16a62
--- /dev/null
+++ b/vendor/google_riscv-dv/test/riscv_instr_test_pkg.sv
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2018 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.
+ */
+
+package riscv_instr_test_pkg;
+
+ import uvm_pkg::*;
+ import riscv_instr_pkg::*;
+
+ `include "uvm_macros.svh"
+ `include "riscv_instr_base_test.sv"
+ `include "riscv_instr_test_lib.sv"
+
+endpackage
diff --git a/vendor/google_riscv-dv/testlist b/vendor/google_riscv-dv/testlist
new file mode 100644
index 00000000..0fd4e31d
--- /dev/null
+++ b/vendor/google_riscv-dv/testlist
@@ -0,0 +1,29 @@
+// Copyright 2018 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.
+
+//================================================
+// Test name : iteration count
+//------------------------------------------------
+riscv_arithmetic_basic_test : 10
+riscv_machine_mode_rand_test : 10
+riscv_privileged_mode_rand_test : 0
+riscv_rand_instr_test : 10
+riscv_rand_jump_test : 10
+riscv_mmu_stress_test : 10
+riscv_page_table_exception_test : 0
+riscv_no_fence_test : 10
+riscv_sfence_exception_test : 0
+riscv_illegal_instr_test : 10
+riscv_hint_instr_test : 10
+//------------------------------------------------
diff --git a/vendor/google_riscv-dv/vcs.compile.option.f b/vendor/google_riscv-dv/vcs.compile.option.f
new file mode 100644
index 00000000..5e32af7d
--- /dev/null
+++ b/vendor/google_riscv-dv/vcs.compile.option.f
@@ -0,0 +1,19 @@
+// Copyright 2018 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.
+
+-sverilog
+-ntb_opts uvm-1.2
+-lca
++define+UVM_REGEX_NO_DPI
+-timescale=1ns/10ps