Update google_riscv-dv to google/riscv-dv@c98d89c (#312)

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

* Interrupt test integration (Udi)
* Update README for illegal/hint instruction (google/riscv-dv#155)
  (taoliug)
* Refactor illegal/hint instruction generation (google/riscv-dv#154)
  (taoliug)
* Skip x0 in GPR save/restore (google/riscv-dv#153) (taoliug)
* Move user_define.h to the beginning of the program (google/riscv-
  dv#151) (taoliug)
* Add user_define.h (google/riscv-dv#149) (taoliug)
* Move instr_bin to a separate section (google/riscv-dv#148) (taoliug)
* Remove temp files (google/riscv-dv#145) (taoliug)
* Move dv_defines.svh outside the package (google/riscv-dv#144)
  (taoliug)
* Fix typo (google/riscv-dv#141) (taoliug)
* Refactored loop instruction stream, reduce global reserved registers
  (google/riscv-dv#139) (taoliug)
* Remove obsolete sample program (google/riscv-dv#138) (taoliug)
* Update readme (google/riscv-dv#137) (taoliug)
* Skip kernel instruction/data pages when not needed (google/riscv-
  dv#136) (taoliug)
* Re-organize data page generation (google/riscv-dv#135) (taoliug)
* Re-organize text and data section (google/riscv-dv#134) (taoliug)
* Refine the bare program mode (google/riscv-dv#133) (taoliug)
* Add a bare program mode (google/riscv-dv#130) (taoliug)
* Allow running riscv-dv from other directories (google/riscv-dv#128)
  (taoliug)
* Fix trace compare issue (google/riscv-dv#123) (taoliug)
* Optimize for constraint solving performance (google/riscv-dv#122)
  (taoliug)
* Avoid ISS simulation timeout (google/riscv-dv#121) (taoliug)
* Optimize irun randomization performance (google/riscv-dv#120)
  (taoliug)
* fix ius compile/simulation warnings (Tao Liu)
* Fix ius compilation failure (Tao Liu)
* Fix google/riscv-dv#109 ius constraint solver failure (Tao Liu)
* Add ebreak sequence generation and cmdline options (Udi)
* Added dret instruction to random generation (Udi)
* Tighten up regex in spike log tracer. (Dave Estes)
* Fix generation of debug handshake (Udi)
* Fix wfi generation, add indent to core_initialization handshake
  (Udi)
This commit is contained in:
udinator 2019-09-13 14:34:56 -07:00 committed by GitHub
parent f025236a22
commit 3fcf5a634d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 861 additions and 19694 deletions

View file

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

View file

@ -184,6 +184,37 @@ riscv_instr_group_t supported_isa[] = {RV32I, RV32M, RV64I, RV64M};
...
```
### Setup the memory map
Here's a few cases that you might want to allocate the instruction and data
sections to match the actual memory map
- The processor has internal memories, and you want to test load/store from
various internal/externel memory regions
- The processor implments the PMP feature, and you want to configure the memory
map to match PMP setting.
- Virtual address translation is implmented and you want to test load/store from
sparse memory locations to verify data TLB replacement logic.
You can configure the memory map in [riscv_instr_gen_config.sv](https://github.com/google/riscv-dv/blob/master/src/riscv_instr_gen_config.sv)
```
mem_region_t mem_region[$] = '{
'{name:"region_0", size_in_bytes: 4096, xwr: 3'b111},
'{name:"region_1", size_in_bytes: 4096 * 4, xwr: 3'b111},
'{name:"region_2", size_in_bytes: 4096 * 2, xwr: 3'b111},
'{name:"region_3", size_in_bytes: 512, xwr: 3'b111},
'{name:"region_4", size_in_bytes: 4096, xwr: 3'b111}
};
```
Each memory region belongs to a separate section in the generated assembly
program. You can modify the link script to link each section to the target
memory location. Please avoid setting a large memory range as it could takes a
long time to randomly initializing the memory. You can break down a large memory
region to a few representative small regions which covers all the boundary
conditions for the load/store testing.
### Runtime options of the generator
| Option | Description | Default |
@ -198,8 +229,8 @@ riscv_instr_group_t supported_isa[] = {RV32I, RV32M, RV64I, RV64M};
| no_load_store | Disable load/store instruction | 0 |
| no_csr_instr | Disable CSR instruction | 0 |
| no_fence | Disable fence instruction | 0 |
| enable_illegal_instruction | Enable illegal instructions | 0 |
| enable_hint_instruction | Enable HINT instruction | 0 |
| illegal_instr_ratio | Number of illegal instructions every 1000 instr | 0 |
| hint_instr_ratio | Number of HINT instructions every 1000 instr | 0 |
| boot_mode | m:Machine mode, s:Supervisor mode, u:User mode | m |
| no_directed_instr | Disable directed instruction stream | 0 |
| require_signature_addr | Set to 1 if test needs to talk to testbench | 0 |
@ -343,9 +374,23 @@ python3 run.py --test riscv_page_table_exception_test --iss new_iss_name
We have collaborated with LowRISC to apply this flow for [IBEX RISC-V core
verification](https://github.com/lowRISC/ibex/blob/master/doc/verification.rst). You can use
it as a reference to setup end-to-end co-simulation flow. It's also a good
reference for [customizing the generator](https://github.com/lowRISC/ibex/tree/master/dv/uvm/riscv_dv_extension) without getting impacted by upstream
changes.
it as a reference to setup end-to-end co-simulation flow.
This repo is still under active development, here's recommended approach to
customize the instruction generator while keeping the minimum effort of merging
upstream changes.
- Do not modify the upstream classes directly. When possible, extending from
the upstream classses and implment your own functionalities.
- Use command line type override to use your extended classes.
--sim_opts="+uvm_set_type_override=<upstream_class>,<extended_class>"
- Create a new file list for your local modifications. Pass to the instruction
generator like below:
--cmp_opts "+define+RISCV_DV_EXT_FILE_LIST=<local_file_list>"
- Create a new file for the core settings, and pass to the instruction generator:
--cmp_opts "+define+RISCV_CORE_SETTING=<your_core_setting.sv>"
You can refer to [riscv-dv extension for ibex](https://github.com/lowRISC/ibex/blob/master/dv/uvm/Makefile#L68) for a working example.
We have plan to open-source the end-to-end environment of other advanced RISC-V
processors. Stay tuned!

View file

@ -13,11 +13,11 @@
// limitations under the License.
// HEADERS
+incdir+./src
+incdir+./test
+incdir+${RISCV_DV_ROOT}/src
+incdir+${RISCV_DV_ROOT}/test
// SOURCES
./src/riscv_signature_pkg.sv
./src/riscv_instr_pkg.sv
./test/riscv_instr_test_pkg.sv
./test/riscv_instr_gen_tb_top.sv
${RISCV_DV_ROOT}/src/riscv_signature_pkg.sv
${RISCV_DV_ROOT}/src/riscv_instr_pkg.sv
${RISCV_DV_ROOT}/test/riscv_instr_test_pkg.sv
${RISCV_DV_ROOT}/test/riscv_instr_gen_tb_top.sv

View file

@ -193,7 +193,7 @@ def gcc_compile(test_list, output_dir, isa, mabi, opts):
"""
for test in test_list:
for i in range(0, test['iterations']):
prefix = ("%s/asm_tests/%s.%d" % (output_dir, test['test'], i))
prefix = ("%s/asm_tests/%s_%d" % (output_dir, test['test'], i))
asm = prefix + ".S"
elf = prefix + ".o"
binary = prefix + ".bin"
@ -201,8 +201,10 @@ def gcc_compile(test_list, output_dir, isa, mabi, opts):
cmd = ("%s -static -mcmodel=medany \
-fvisibility=hidden -nostdlib \
-nostartfiles %s \
-Tscripts/link.ld %s -o %s " % \
(get_env_var("RISCV_GCC"), asm, opts, elf))
-I%s/user_extension \
-T%s/scripts/link.ld %s -o %s " % \
(get_env_var("RISCV_GCC"), asm, get_env_var("RISCV_DV_ROOT"),
get_env_var("RISCV_DV_ROOT"), opts, elf))
if 'gcc_opts' in test:
cmd += test['gcc_opts']
# If march/mabi is not defined in the test gcc_opts, use the default
@ -243,7 +245,7 @@ def iss_sim(test_list, output_dir, iss_list, iss_yaml, isa, timeout_s):
continue
else:
for i in range(0, test['iterations']):
prefix = ("%s/asm_tests/%s.%d" % (output_dir, test['test'], i))
prefix = ("%s/asm_tests/%s_%d" % (output_dir, test['test'], i))
elf = prefix + ".o"
log = ("%s/%s.%d.log" % (log_dir, test['test'], i))
cmd = get_iss_cmd(base_cmd, elf, log)
@ -268,7 +270,7 @@ def iss_cmp(test_list, iss, output_dir, isa):
run_cmd("rm -rf %s" % report)
for test in test_list:
for i in range(0, test['iterations']):
elf = ("%s/asm_tests/%s.%d.o" % (output_dir, test['test'], i))
elf = ("%s/asm_tests/%s_%d.o" % (output_dir, test['test'], i))
logging.info("Comparing ISS sim result %s/%s : %s" %
(iss_list[0], iss_list[1], elf))
csv_list = []
@ -342,7 +344,7 @@ def setup_parser():
help="Generator timeout limit in seconds")
parser.add_argument("--end_signature_addr", type=str, default="0",
help="Address that privileged CSR test writes to at EOT")
parser.add_argument("--iss_timeout", type=int, default=50,
parser.add_argument("--iss_timeout", type=int, default=25,
help="ISS sim timeout limit in seconds")
parser.add_argument("--iss_yaml", type=str, default="",
help="ISS setting YAML")
@ -376,7 +378,7 @@ def setup_logging(verbose):
def main():
"""This is the main entry point."""
check_riscv_dv_setting()
parser = setup_parser()
args = parser.parse_args()
cwd = os.path.dirname(os.path.realpath(__file__))
@ -399,6 +401,7 @@ def main():
output_dir = "out_" + str(date.today())
else:
output_dir = args.o
subprocess.run(["mkdir", "-p", output_dir])
subprocess.run(["mkdir", "-p", ("%s/asm_tests" % output_dir)])

File diff suppressed because it is too large Load diff

View file

@ -279,7 +279,7 @@ def gen_csr_instr(original_csr_map, csr_instructions, xlen,
csr_map = copy.deepcopy(original_csr_map)
source_reg, dest_reg = [f"x{i}" for i in random.sample(range(1, 16), 2)]
csr_list = list(csr_map.keys())
with open(f"{out}/riscv_csr_test.{i}.S", "w") as csr_test_file:
with open(f"{out}/riscv_csr_test_{i}.S", "w") as csr_test_file:
gen_setup(csr_test_file)
for csr in csr_list:
csr_address, csr_val, csr_write_mask, csr_read_mask = csr_map.get(csr)

View file

@ -201,6 +201,8 @@ def parse_gpr_update_from_trace(trace_csv, gpr_trace):
def check_update_gpr(rd, rd_val, gpr):
gpr_state_change = 0
if rd == '':
return gpr_state_change
if rd in gpr:
if rd_val != gpr[rd]:
gpr_state_change = 1

View file

@ -59,6 +59,15 @@ def get_env_var(var):
return val
def check_riscv_dv_setting():
"""Check the RISCV-DV directory setting, default "."
"""
try:
val = os.environ["RISCV_DV_ROOT"]
except KeyError:
os.environ["RISCV_DV_ROOT"] = "."
def get_seed(seed):
"""Get the seed to run the generator

View file

@ -19,13 +19,15 @@ ENTRY(_start)
SECTIONS
{
. = 0x80000000;
.text.init : { *(.text.init) }
.text : { *(.text) }
. = ALIGN(0x1000);
.tohost : { *(.tohost) }
. = ALIGN(0x1000);
.text : { *(.text) }
. = ALIGN(0x1000);
.page_table : { *(.page_table) }
.data : { *(.data) }
.user_stack : { *(.user_stack) }
.kernel_data : { *(.kernel_data) }
.kernel_stack : { *(.kernel_stack) }
.bss : { *(.bss) }
_end = .;
}

View file

@ -37,7 +37,7 @@ def process_spike_sim_log(spike_log, csv):
spike_instr = ""
RD_RE = re.compile(r"(?P<pri>\d) 0x(?P<addr>[a-f0-9]+?) " \
"\((?P<bin>.*?)\) x\s*(?P<reg>\d*?) 0x(?P<val>.*)")
"\((?P<bin>.*?)\) x\s*(?P<reg>\d*?) 0x(?P<val>[a-f0-9]+)")
CORE_RE = re.compile(r"core.*0x(?P<addr>[a-f0-9]+?) \(0x(?P<bin>.*?)\) (?P<instr>.*?)$")
INSTR_RE = re.compile(r"(?P<instr>[a-z\.]+?)(\s+?)(?P<operand>.*)")
GPR_RE = re.compile(r"^[a-z][0-9a-z]$")

View file

@ -35,6 +35,10 @@ riscv_instr_group_t supported_isa[$] = {RV32I, RV32M, RV64I, RV64M, RV32C, RV64C
// Interrupt mode support
mtvec_mode_t supported_interrupt_mode[$] = {DIRECT, VECTORED};
// The number of interrupt vectors to be generated, only used if VECTORED interrupt mode is
// supported
int max_interrupt_vector_num = 16;
// Debug mode support
bit support_debug_mode = 0;
@ -44,48 +48,6 @@ bit support_umode_trap = 0;
// Support sfence.vma instruction
bit support_sfence = 1;
// 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 = 40;
// 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);
// The maximum data section byte size actually used by load/store instruction
// Set to this value to be smaller than data_page_size. If there's data cache in the system,
// this value should be set large enough to be able to hit cache hit/miss scenario within a data
// section. Don't set this to too big as it will introduce a very large binary.
int max_used_data_page_size = 512;
// 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 = 5;
// 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;
// ----------------------------------------------------------------------------
// Previleged CSR implementation
// ----------------------------------------------------------------------------

View file

@ -15,7 +15,7 @@
*/
// Base class for AMO instruction stream
class riscv_amo_base_instr_stream extends riscv_directed_instr_stream;
class riscv_amo_base_instr_stream extends riscv_mem_access_stream;
rand int unsigned num_amo;
rand int unsigned num_mixed_instr;
@ -23,6 +23,7 @@ class riscv_amo_base_instr_stream extends riscv_directed_instr_stream;
rand riscv_reg_t rs1_reg;
riscv_reg_t reserved_rd[$];
rand int unsigned data_page_id;
rand int max_load_store_offset;
// User can specify a small group of available registers to generate various hazard condition
rand riscv_reg_t avail_regs[];
@ -70,10 +71,10 @@ class riscv_amo_base_instr_stream extends riscv_directed_instr_stream;
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);
if(kernel_mode) begin
la_instr.imm_str = cfg.s_mem_region[data_page_id].name;
end else begin
la_instr.imm_str = $sformatf("kernel_data_page_%0d+%0d", data_page_id, base);
la_instr.imm_str = cfg.mem_region[data_page_id].name;
end
instr_list.push_front(la_instr);
endfunction

View file

@ -29,8 +29,6 @@ class riscv_asm_program_gen extends uvm_object;
// 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;
riscv_instr_sequence debug_program;
riscv_instr_sequence debug_sub_program[];
string instr_binary[$];
@ -70,30 +68,12 @@ class riscv_asm_program_gen extends uvm_object;
gen_program_header();
// Initialize general purpose registers
init_gpr();
setup_misa();
// 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.is_debug_program = 0;
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));
bin_program.post_process_instr();
bin_program.generate_binary_stream(instr_binary);
if (!cfg.bare_program_mode) begin
setup_misa();
// Create all page tables
create_page_table();
// Setup privileged mode registers and enter target privileged mode
pre_enter_privileged_mode();
end
// Init section
gen_init_section();
@ -113,16 +93,17 @@ class riscv_asm_program_gen extends uvm_object;
main_program.gen_instr(1);
// Setup jump instruction among main program and sub programs
gen_callstack(main_program, sub_program, sub_program_name, cfg.num_of_sub_program);
if (bin_program != null) begin
main_program.insert_jump_instr("sub_bin", 0);
end
`uvm_info(`gfn, "Generating callstack...done", UVM_LOW)
main_program.post_process_instr();
`uvm_info(`gfn, "Post-processing main program...done", UVM_LOW)
main_program.generate_instr_stream();
`uvm_info(`gfn, "Generating main program instruction stream...done", UVM_LOW)
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
insert_sub_program(sub_program, instr_stream);
`uvm_info(`gfn, "Inserting sub-programs...done", UVM_LOW)
// Reserve some space to copy instruction from data section
if (instr_binary.size() > 0) begin
instr_stream.push_back(".align 2");
@ -132,45 +113,49 @@ class riscv_asm_program_gen extends uvm_object;
instr_stream.push_back({indent, ".endr"});
instr_stream.push_back({indent, "ret"});
end
// Privileged mode switch routine
gen_privileged_mode_switch_routine();
`uvm_info(`gfn, "Main/sub program generation...done", UVM_LOW)
// Program end
gen_program_end();
// Generate debug rom section
gen_debug_rom();
// Generate debug mode exception handler
gen_debug_exception_handler();
if (!cfg.bare_program_mode) begin
// Privileged mode switch routine
gen_privileged_mode_switch_routine();
// Generate debug rom section
gen_debug_rom();
// Generate debug mode exception handler
gen_debug_exception_handler();
end
// Starting point of data section
gen_data_page_begin();
// Generate the sub program in binary format
gen_bin_program();
// Page table
gen_page_table_section();
if (!cfg.bare_program_mode) begin
gen_page_table_section();
end
if(!cfg.no_data_page) begin
// Data section
// Kernel data section
gen_data_page();
end
gen_data_page_end();
// Stack section
gen_stack_section();
// Generate kernel program/data/stack section
gen_kernel_sections();
if (!cfg.bare_program_mode) begin
// Generate kernel program/data/stack section
gen_kernel_sections();
end
endfunction
//---------------------------------------------------------------------------------------
// Generate kernel program/data/stack sections
//---------------------------------------------------------------------------------------
virtual function void gen_kernel_sections();
instr_stream.push_back("_kernel_start: .align 12");
instr_stream.push_back("_kernel_instr_start: .align 12");
instr_stream.push_back(".text");
// Kernel programs
if (cfg.init_privileged_mode != MACHINE_MODE) begin
if (cfg.virtual_addr_translation_on) begin
smode_accessible_umode_program = riscv_instr_sequence::type_id::
create("smode_accessible_umode_program");
gen_kernel_program(smode_accessible_umode_program);
end
smode_program = riscv_instr_sequence::type_id::create("smode_program");
gen_kernel_program(smode_program);
if (cfg.init_privileged_mode != MACHINE_MODE) begin
smode_program = riscv_instr_sequence::type_id::create("smode_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);
end
@ -182,25 +167,29 @@ class riscv_asm_program_gen extends uvm_object;
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));
instr_stream.push_back("_kernel_instr_end: nop");
// User stack and data pages may not be accessible when executing trap handling programs in
// machine/supervisor mode. Generate separate kernel data/stack sections to solve it.
if (cfg.virtual_addr_translation_on) begin
// Kernel data pages
instr_stream.push_back("_kernel_data_start: .align 12");
if(!cfg.no_data_page) begin
// Data section
gen_data_page(1'b1);
end
gen_data_page_end();
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;
seq.instr_cnt = cfg.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));
.kernel_mode(1'b1));
seq.label_name = seq.get_name();
seq.is_debug_program = 0;
seq.cfg = cfg;
@ -290,10 +279,9 @@ class riscv_asm_program_gen extends uvm_object;
//---------------------------------------------------------------------------------------
virtual function void gen_program_header();
instr_stream.push_back(".macro init");
instr_stream.push_back(".endm");
instr_stream.push_back(".section .text.init");
instr_stream.push_back(".include \"user_define.h\"");
instr_stream.push_back(".globl _start");
instr_stream.push_back(".section .text");
instr_stream.push_back("_start:");
endfunction
@ -309,12 +297,6 @@ class riscv_asm_program_gen extends uvm_object;
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);
@ -331,24 +313,28 @@ class riscv_asm_program_gen extends uvm_object;
// Generate the user stack section
virtual function void gen_stack_section();
instr_stream.push_back(".pushsection .user_stack,\"aw\",@progbits;");
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(".rept %0d", cfg.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));
instr_stream.push_back(".popsection;");
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(".pushsection .kernel_stack,\"aw\",@progbits;");
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(".rept %0d", cfg.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));
instr_stream.push_back(".popsection;");
endfunction
virtual function void gen_init_section();
@ -399,10 +385,12 @@ class riscv_asm_program_gen extends uvm_object;
// Write to the signature_addr with values to indicate to the core testbench
// that is safe to start sending interrupt and debug stimulus
virtual function void core_is_initialized();
string instr[$];
if (cfg.require_signature_addr) begin
if (cfg.signature_addr != 32'hdead_beef) begin
string str;
gen_signature_handshake(instr_stream, CORE_STATUS, INITIALIZED);
gen_signature_handshake(instr, CORE_STATUS, INITIALIZED);
format_section(instr);
instr_stream = {instr_stream, instr};
end else begin
`uvm_fatal(`gfn, "The signature_addr is not properly configured!")
end
@ -434,7 +422,11 @@ class riscv_asm_program_gen extends uvm_object;
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"});
if (cfg.bare_program_mode) begin
instr_stream.push_back({indent, "j write_tohost"});
end else begin
instr_stream.push_back({indent, "ecall"});
end
endfunction
// Dump all GPR to the starting point of the program
@ -474,7 +466,7 @@ class riscv_asm_program_gen extends uvm_object;
// 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
if(cfg.virtual_addr_translation_on) begin
page_table_list.process_page_table(instr);
gen_section("process_pt", instr);
end
@ -486,6 +478,8 @@ class riscv_asm_program_gen extends uvm_object;
privil_seq = riscv_privileged_common_seq::type_id::create("privil_seq");
foreach(riscv_instr_pkg::supported_privileged_mode[i]) begin
string instr[$];
string csr_handshake[$];
string ret_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)
@ -493,6 +487,27 @@ class riscv_asm_program_gen extends uvm_object;
privil_seq.cfg = cfg;
`DV_CHECK_RANDOMIZE_FATAL(privil_seq)
privil_seq.enter_privileged_mode(riscv_instr_pkg::supported_privileged_mode[i], instr);
if (cfg.require_signature_addr) begin
ret_instr = instr.pop_back();
// Want to write the main system CSRs to the testbench before indicating that initialization
// is complete, for any initial state analysis
case(riscv_instr_pkg::supported_privileged_mode[i])
MACHINE_MODE: begin
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(MSTATUS));
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(MIE));
end
SUPERVISOR_MODE: begin
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(SSTATUS));
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(SIE));
end
USER_MODE: begin
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(USTATUS));
gen_signature_handshake(.instr(csr_handshake), .signature_type(WRITE_CSR), .csr(UIE));
end
endcase
format_section(csr_handshake);
instr = {instr, csr_handshake, ret_instr};
end
instr_stream = {instr_stream, instr};
end
endfunction
@ -502,7 +517,7 @@ class riscv_asm_program_gen extends uvm_object;
string instr[];
string mode_name;
instr = {"la x10, _init"};
if(SATP_MODE != BARE && cfg.init_privileged_mode != MACHINE_MODE) begin
if(cfg.virtual_addr_translation_on) begin
// For supervisor and user mode, use virtual address instead of physical address.
// Virtual address starts from address 0x0, here only the lower 12 bits are kept
// as virtual address offset.
@ -582,7 +597,7 @@ class riscv_asm_program_gen extends uvm_object;
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
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.
@ -607,12 +622,12 @@ class riscv_asm_program_gen extends uvm_object;
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);
gen_trap_handler_section("m", MCAUSE, MTVEC, MTVAL, MEPC, MSCRATCH, MSTATUS, MIE, MIP);
SUPERVISOR_MODE:
gen_trap_handler_section("s", SCAUSE, STVEC, STVAL, SEPC, SSCRATCH, SSTATUS);
gen_trap_handler_section("s", SCAUSE, STVEC, STVAL, SEPC, SSCRATCH, SSTATUS, SIE, SIP);
USER_MODE:
if(riscv_instr_pkg::support_umode_trap)
gen_trap_handler_section("u", UCAUSE, UTVEC, UTVAL, UEPC, USCRATCH, USTATUS);
gen_trap_handler_section("u", UCAUSE, UTVEC, UTVAL, UEPC, USCRATCH, USTATUS, UIE, UIP);
endcase
end
// Ebreak handler
@ -639,12 +654,13 @@ class riscv_asm_program_gen extends uvm_object;
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);
privileged_reg_t scratch, privileged_reg_t status,
privileged_reg_t ie, privileged_reg_t ip);
bit is_interrupt = 'b1;
string tvec_name;
string instr[$];
if (cfg.mtvec_mode == VECTORED) begin
gen_interrupt_vector_table(mode, status, cause, scratch, instr);
gen_interrupt_vector_table(mode, status, cause, ie, ip, scratch, instr);
end else begin
// Push user mode GPR to kernel stack before executing exception handling, this is to avoid
// exception handling routine modify user program state unexpectedly
@ -716,6 +732,8 @@ class riscv_asm_program_gen extends uvm_object;
virtual function void gen_interrupt_vector_table(string mode,
privileged_reg_t status,
privileged_reg_t cause,
privileged_reg_t ie,
privileged_reg_t ip,
privileged_reg_t scratch,
ref string instr[$]);
@ -727,22 +745,27 @@ class riscv_asm_program_gen extends uvm_object;
instr = {instr, ".option norvc;",
$sformatf("j %0smode_exception_handler", mode)};
// Redirect the interrupt to the corresponding interrupt handler
for (int i = 1; i < 16; i++) begin
for (int i = 1; i < max_interrupt_vector_num; i++) begin
instr.push_back($sformatf("j %0smode_intr_vector_%0d", mode, i));
end
instr = {instr, ".option rvc;"};
for (int i = 1; i < 16; i++) begin
for (int i = 1; i < max_interrupt_vector_num; i++) begin
string intr_handler[$];
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, intr_handler);
gen_signature_handshake(intr_handler, CORE_STATUS, HANDLING_IRQ);
gen_signature_handshake(.instr(intr_handler), .signature_type(CORE_STATUS), .core_status(HANDLING_IRQ));
intr_handler = {intr_handler,
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
// Terminate the test if xCause[31] != 0 (indicating exception)
$sformatf("bltz a1, 1f"),
// TODO(taliu) write xCause to the signature address
$sformatf("csrr a1, 0x%0x # %0s", cause, cause.name()),
// Terminate the test if xCause[31] != 0 (indicating exception)
$sformatf("srli a1, a1, 0x1f"),
$sformatf("beqz a1, 1f")};
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(status));
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(cause));
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(ie));
gen_signature_handshake(.instr(intr_handler), .signature_type(WRITE_CSR), .csr(ip));
// Jump to commmon interrupt handling routine
$sformatf("j %0smode_intr_handler", mode),
"1: j test_done"};
intr_handler = {intr_handler,
$sformatf("j %0smode_intr_handler", mode),
"1: j test_done"};
gen_section($sformatf("%0smode_intr_vector_%0d", mode, i), intr_handler);
end
endfunction
@ -768,9 +791,11 @@ class riscv_asm_program_gen extends uvm_object;
// guarantees that epc + 4 is a valid instruction boundary
// TODO: Support random operations in debug mode
// TODO: Support ebreak exception delegation
// TODO: handshake the correct Xcause CSR based on delegation privil. mode
virtual function void gen_ebreak_handler();
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION);
gen_signature_handshake(instr, CORE_STATUS, EBREAK_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
"csrr x31, mepc",
"addi x31, x31, 4",
@ -787,9 +812,11 @@ class riscv_asm_program_gen extends uvm_object;
// 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.
// TODO: handshake the corret Xcause CSR based on delegation setup
virtual function void gen_illegal_instr_handler();
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION);
gen_signature_handshake(instr, CORE_STATUS, ILLEGAL_INSTR_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
"csrr x31, mepc",
"addi x31, x31, 4",
@ -810,7 +837,7 @@ class riscv_asm_program_gen extends uvm_object;
// 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
if(cfg.virtual_addr_translation_on) begin
page_table_list = riscv_page_table_list#(SATP_MODE)::
type_id::create("page_table_list");
page_table_list.cfg = cfg;
@ -830,10 +857,12 @@ class riscv_asm_program_gen extends uvm_object;
virtual function void gen_page_table_section();
string page_table_section[$];
if(page_table_list != null) begin
instr_stream.push_back(".pushsection .page_table,\"aw\",@progbits;");
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
instr_stream.push_back(".popsection;");
end
endfunction
@ -841,6 +870,10 @@ class riscv_asm_program_gen extends uvm_object;
// 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[$]);
// Utilize the memory mapped handshake scheme to signal the testbench that the interrupt
// handling has been completed and we are about to xRET out of the handler
gen_signature_handshake(.instr(interrupt_handler_instr), .signature_type(CORE_STATUS),
.core_status(FINISHED_IRQ));
endfunction
// Interrupt handler routine
@ -943,39 +976,34 @@ class riscv_asm_program_gen extends uvm_object;
input signature_type_t signature_type,
core_status_t core_status = INITIALIZED,
test_result_t test_result = TEST_FAIL,
privileged_reg_t csr = MSCRATCH);
privileged_reg_t csr = MSCRATCH,
string addr_label = "");
if (cfg.require_signature_addr) begin
string str;
str = $sformatf("li x%0d, 0x%0h", cfg.signature_addr_reg, cfg.signature_addr);
instr.push_back(str);
string str[$];
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_addr_reg, cfg.signature_addr)};
instr = {instr, str};
case (signature_type)
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of CORE_STATUS, and the upper
// XLEN-8 bits contain the core_status_t data.
CORE_STATUS: begin
str = $sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, core_status);
instr.push_back(str);
str = $sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg);
instr.push_back(str);
str = $sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type);
instr.push_back(str);
str = $sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg);
instr.push_back(str);
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, core_status),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
instr = {instr, str};
end
// A single data word is written to the signature address.
// Bits [7:0] contain the signature_type of TEST_RESULT, and the upper
// XLEN-8 bits contain the test_result_t data.
TEST_RESULT: begin
str = $sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, test_result);
instr.push_back(str);
str = $sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg);
instr.push_back(str);
str = $sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type);
instr.push_back(str);
str = $sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg);
instr.push_back(str);
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, test_result),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
instr = {instr, str};
end
// The first write to the signature address contains just the
// signature_type of WRITE_GPR.
@ -983,13 +1011,12 @@ class riscv_asm_program_gen extends uvm_object;
// each writing the data contained in one GPR, starting from x0 as the
// first write, and ending with x31 as the 32nd write.
WRITE_GPR: begin
str = $sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, signature_type);
instr.push_back(str);
str = $sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg);
instr.push_back(str);
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
instr = {instr, str};
for(int i = 0; i < 32; i++) begin
str = $sformatf("sw x%0x, 0(x%0d)", i, cfg.signature_addr_reg);
instr.push_back(str);
str = {$sformatf("sw x%0x, 0(x%0d)", i, cfg.signature_addr_reg)};
instr = {instr, str};
end
end
// The first write to the signature address contains the
@ -998,19 +1025,14 @@ class riscv_asm_program_gen extends uvm_object;
// It is followed by a second write to the signature address,
// containing the data stored in the specified CSR.
WRITE_CSR: begin
str = $sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, csr);
instr.push_back(str);
str = $sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg);
instr.push_back(str);
str = $sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type);
instr.push_back(str);
str = $sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg);
instr.push_back(str);
str = $sformatf("csrr x%0d, 0x%0h", cfg.signature_data_reg, csr);
instr.push_back(str);
str = $sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg);
instr.push_back(str);
str = {$sformatf("li x%0d, 0x%0h", cfg.signature_data_reg, csr),
$sformatf("slli x%0d, x%0d, 8", cfg.signature_data_reg, cfg.signature_data_reg),
$sformatf("addi x%0d, x%0d, 0x%0h", cfg.signature_data_reg,
cfg.signature_data_reg, signature_type),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg),
$sformatf("csrr x%0d, 0x%0h", cfg.signature_data_reg, csr),
$sformatf("sw x%0d, 0(x%0d)", cfg.signature_data_reg, cfg.signature_addr_reg)};
instr = {instr, str};
end
default: begin
`uvm_fatal(`gfn, "signature_type is not defined")
@ -1048,7 +1070,7 @@ class riscv_asm_program_gen extends uvm_object;
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,
input bit kernel_mode = 0,
output riscv_instr_stream instr_stream[]);
uvm_object object_h;
riscv_rand_instr_stream new_instr_stream;
@ -1073,7 +1095,7 @@ class riscv_asm_program_gen extends uvm_object;
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;
new_instr_stream.kernel_mode = kernel_mode;
`DV_CHECK_RANDOMIZE_FATAL(new_instr_stream)
instr_stream = {instr_stream, new_instr_stream};
end else begin
@ -1085,45 +1107,84 @@ class riscv_asm_program_gen extends uvm_object;
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
//---------------------------------------------------------------------------------------
// Generate the debug rom, and any related programs
//---------------------------------------------------------------------------------------
// Generate the program in the debug ROM
// Processor will fetch instruction from here upon receiving debug request from debug module
virtual function void gen_debug_rom();
string push_gpr[$];
string pop_gpr[$];
string instr[$];
string debug_end[$];
string dret;
string debug_sub_program_name[$] = {};
string str[$];
if (riscv_instr_pkg::support_debug_mode) begin
// Signal that the core entered the debug rom regardless of whether the
// main debug rom program has been generated
gen_signature_handshake(instr, CORE_STATUS, IN_DEBUG_MODE);
format_section(instr);
dret = {format_string(" ", LABEL_STR_LEN), "dret"};
// The main debug rom
if (cfg.gen_debug_section) begin
if (!cfg.gen_debug_section) begin
// If the debug section should not be generated, we just populate it
// with a dret instruction.
instr = {dret};
gen_section("debug_rom", instr);
end else begin
if (cfg.enable_ebreak_in_debug_rom) begin
// As execution of ebreak in D mode causes core to
// re-enter D mode, this directed sequence will be a loop that ensures the
// ebreak instruction will only be executed once to prevent infinitely
// looping back to the beginning of the debug rom.
// Write dscratch to random GPR and branch to debug_end if greater
// than 0, for ebreak loops.
// Use dscratch1 to store original GPR value.
str = {$sformatf("csrw 0x%0x, x%0d", DSCRATCH1, cfg.scratch_reg),
$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH0)};
instr = {instr, str};
// send dpc and dcsr to testbench, as this handshake will be
// executed twice due to the ebreak loop, there should be no change
// in their values as by the Debug Mode Spec Ch. 4.1.8
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DCSR));
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DPC));
str = {$sformatf("beq x%0d, x0, 1f", cfg.scratch_reg),
$sformatf("j debug_end"),
$sformatf("1: csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
instr = {instr, str};
end
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, instr);
// Signal that the core entered debug rom only if the rom is actually
// being filled with random instructions to prevent stress tests from
// having to execute unnecessary push/pop of GPRs on the stack ever
// time a debug request is sent
gen_signature_handshake(instr, CORE_STATUS, IN_DEBUG_MODE);
if (cfg.set_dcsr_ebreak) begin
// We want to set dcsr.ebreak(m/s/u) to 1'b1, depending on what modes
// are available.
// TODO(udinator) - randomize the dcsr.ebreak setup
gen_dcsr_ebreak(instr);
end
// Check dcsr.cause, and update dpc by 0x4 if the cause is ebreak, as
// ebreak will set set dpc to its own address, which will cause an
// infinite loop.
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DCSR),
$sformatf("slli x%0d, x%0d, 0x17", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("srli x%0d, x%0d, 0x1d", cfg.scratch_reg, cfg.scratch_reg),
$sformatf("li x%0d, 0x1", cfg.signature_data_reg),
$sformatf("bne x%0d, x%0d, 2f", cfg.scratch_reg, cfg.signature_data_reg)};
instr = {instr, str};
increment_csr(DPC, 4, instr);
str = {"2: nop"};
instr = {instr, str};
// write DCSR to the testbench for any analysis
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(DCSR));
// Increment dscratch0 by 1 to update the loop counter for all ebreak
// tests
if (cfg.enable_ebreak_in_debug_rom || cfg.set_dcsr_ebreak) begin
// Add 1 to dscratch0
increment_csr(DSCRATCH0, 1, instr);
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
instr = {instr, str};
end
format_section(instr);
gen_sub_program(debug_sub_program, debug_sub_program_name,
cfg.num_debug_sub_program, 1'b1, "debug_sub");
debug_program = riscv_instr_sequence::type_id::create("debug_program");
@ -1136,17 +1197,28 @@ class riscv_asm_program_gen extends uvm_object;
cfg.num_debug_sub_program);
debug_program.post_process_instr();
debug_program.generate_instr_stream(.no_label(1'b1));
// Need to save off GPRs to avoid modifying program flow
push_gpr_to_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, push_gpr);
format_section(push_gpr);
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, pop_gpr);
format_section(pop_gpr);
instr = {push_gpr, instr, debug_program.instr_string_list, pop_gpr};
insert_sub_program(debug_sub_program, instr_stream);
instr = {instr, debug_program.instr_string_list};
gen_section("debug_rom", instr);
// Set dscratch0 back to 0x0 to prepare for the next entry into debug
// mode, and write dscratch0 and dcsr to the testbench for any
// analysis
if (cfg.enable_ebreak_in_debug_rom) begin
str = {$sformatf("csrwi 0x%0x, 0x0", DSCRATCH0)};
debug_end = {debug_end, str};
end
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, debug_end);
// We have been using dscratch1 to store the
// value of our given scratch register for use in ebreak loop, so we
// need to restore its value before returning from D mode
if (cfg.enable_ebreak_in_debug_rom) begin
str = {$sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, DSCRATCH1)};
debug_end = {debug_end, str};
end
format_section(debug_end);
debug_end = {debug_end, dret};
gen_section("debug_end", debug_end);
end
dret = {format_string(" ", LABEL_STR_LEN), "dret"};
instr = {instr, dret};
gen_section("debug_rom", instr);
end
endfunction
@ -1159,4 +1231,38 @@ class riscv_asm_program_gen extends uvm_object;
end
endfunction
// Set dcsr.ebreak(m/s/u)
// TODO(udinator) - randomize the setup for these fields
virtual function void gen_dcsr_ebreak(ref string instr[$]);
string str;
if (MACHINE_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x8000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
if (SUPERVISOR_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x2000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
if (USER_MODE inside {riscv_instr_pkg::supported_privileged_mode}) begin
str = $sformatf("li x%0d, 0x1000", cfg.scratch_reg);
instr.push_back(str);
str = $sformatf("csrs dcsr, x%0d", cfg.scratch_reg);
instr.push_back(str);
end
endfunction
virtual function void increment_csr(privileged_reg_t csr, int val, ref string instr[$]);
string str;
str = $sformatf("csrr x%0d, 0x%0x", cfg.scratch_reg, csr);
instr.push_back(str);
str = $sformatf("addi x%0d, x%0d, 0x%0x", cfg.scratch_reg, cfg.scratch_reg, val);
instr.push_back(str);
str = $sformatf("csrw 0x%0x, x%0d", csr, cfg.scratch_reg);
instr.push_back(str);
endfunction
endclass

View file

@ -23,6 +23,7 @@ class riscv_data_page_gen extends uvm_object;
riscv_instr_gen_config cfg;
string data_page_str[$];
mem_region_t mem_region_setting[$];
`uvm_object_utils(riscv_data_page_gen)
@ -48,29 +49,48 @@ class riscv_data_page_gen extends uvm_object;
end
endfunction
// Generate the assembly code for the data section
// Generate data pages for all memory regions
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));
if (is_kernel) begin
mem_region_setting = cfg.s_mem_region;
end else begin
mem_region_setting = cfg.mem_region;
end
if (is_kernel) begin
// All kernel data pages in the same section
data_page_str.push_back(".pushsection .kernel_data,\"aw\",@progbits;");
end
foreach (mem_region_setting[i]) begin
`uvm_info(`gfn, $sformatf("Generate data section: %0s size:0x%0x xwr:0x%0x]",
mem_region_setting[i].name,
mem_region_setting[i].size_in_bytes,
mem_region_setting[i].xwr), UVM_LOW)
if (!is_kernel) begin
data_page_str.push_back($sformatf(".pushsection .%0s,\"aw\",@progbits;",
mem_region_setting[i].name));
end
data_page_str.push_back($sformatf(".align %0d", riscv_instr_pkg::data_page_alignment));
data_page_str.push_back($sformatf("%0s:", mem_region_setting[i].name));
page_size = mem_region_setting[i].size_in_bytes;
for(int i = 0; i < page_size; i+= 32) begin
gen_data(.idx(i), .pattern(pattern), .num_of_bytes(32), .data(tmp_data));
if (page_size-i >= 32) begin
gen_data(.idx(i), .pattern(pattern), .num_of_bytes(32), .data(tmp_data));
end else begin
gen_data(.idx(i), .pattern(pattern), .num_of_bytes(page_size-i), .data(tmp_data));
end
tmp_str = format_string($sformatf(".word %0s", format_data(tmp_data)), LABEL_STR_LEN);
data_page_str.push_back(tmp_str);
end
if (!is_kernel) begin
data_page_str.push_back(".popsection;");
end
end
if (is_kernel) begin
data_page_str.push_back(".popsection;");
end
endfunction

View file

@ -40,6 +40,27 @@ class riscv_directed_instr_stream extends riscv_rand_instr_stream;
endclass
// Base class for memory access stream
class riscv_mem_access_stream extends riscv_directed_instr_stream;
int max_data_page_id;
mem_region_t data_page[$];
string label;
`uvm_object_utils(riscv_mem_access_stream)
`uvm_object_new
function void pre_randomize();
if(kernel_mode) begin
data_page = cfg.s_mem_region;
end else begin
data_page = cfg.mem_region;
end
max_data_page_id = data_page.size();
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;
@ -109,10 +130,7 @@ class riscv_jump_instr extends riscv_rand_instr_stream;
!(addi.rs1 inside {cfg.reserved_regs, ZERO});
addi.rs1 == la.rd;
addi.rd == la.rd;
// Avoid using negative offset -1024
addi.imm != 'hFFFF_FC00;
addi.imm != 1024;
jump.imm == ~addi.imm + 1;
imm inside {[-1023:1023]};
jump.rs1 == addi.rd;
addi.instr_name == ADDI;
branch.category == BRANCH;
@ -142,6 +160,8 @@ class riscv_jump_instr extends riscv_rand_instr_stream;
initialize_instr_list(mixed_instr_cnt);
gen_instr(1'b1);
la.imm_str = target_program_label;
addi.imm_str = $sformatf("%0d", imm);
jump.imm_str = $sformatf("%0d", -imm);
// 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.
@ -188,8 +208,9 @@ class riscv_push_stack_instr extends riscv_rand_instr_stream;
endfunction
function void init();
// Save RA, T0 and all reserved loop regs
saved_regs = {RA, T0, cfg.loop_regs};
// Save RA, T0
reserved_rd = {RA, T0};
saved_regs = {RA, T0};
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",
@ -266,6 +287,7 @@ class riscv_pop_stack_instr extends riscv_rand_instr_stream;
endfunction
function void init();
reserved_rd = {RA, T0};
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",
@ -337,7 +359,8 @@ class riscv_long_branch_instr extends riscv_rand_instr_stream;
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);
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
bit is_debug_program = 1'b0);
int branch_offset;
super.gen_instr(1'b1);
forward_branch_instr_stream.gen_instr();

View file

@ -39,9 +39,10 @@ class riscv_illegal_instr extends uvm_object;
7'b0100011,
7'b0110111,
7'b1100011,
7'b0110011,
7'b1100111,
7'b1101111,
7'b1110011};
7'b1110011,
7'b1101111};
rand illegal_instr_type_e exception;
rand bit [31:0] instr_bin;
@ -213,9 +214,6 @@ class riscv_illegal_instr extends uvm_object;
if (riscv_instr_pkg::RV32A inside {riscv_instr_pkg::supported_isa}) begin
legal_opcode = {legal_opcode, 7'b0101111};
end
if (riscv_instr_pkg::RV32M inside {riscv_instr_pkg::supported_isa}) begin
legal_opcode = {legal_opcode, 7'b0110011};
end
if ((riscv_instr_pkg::RV64I inside {riscv_instr_pkg::supported_isa}) ||
riscv_instr_pkg::RV64M inside {riscv_instr_pkg::supported_isa}) begin
legal_opcode = {legal_opcode, 7'b0111011};

View file

@ -182,6 +182,9 @@ class riscv_instr_base extends uvm_object;
rs2 inside {[S0:A5]};
rd inside {[S0:A5]};
}
if(format inside {CI_FORMAT, CR_FORMAT}) {
rs1 == rd;
}
// C_ADDI16SP is only valid when rd == SP
if(instr_name == C_ADDI16SP) {
rd == SP;
@ -244,6 +247,7 @@ class riscv_instr_base extends uvm_object;
`add_instr(URET, I_FORMAT, SYSTEM, RV32I)
`add_instr(SRET, I_FORMAT, SYSTEM, RV32I)
`add_instr(MRET, I_FORMAT, SYSTEM, RV32I)
`add_instr(DRET, I_FORMAT, SYSTEM, RV32I)
`add_instr(WFI, I_FORMAT, INTERRUPT, RV32I)
// CSR instructions
`add_instr(CSRRW, R_FORMAT, CSR, RV32I, UIMM)
@ -469,16 +473,35 @@ class riscv_instr_base extends uvm_object;
function riscv_reg_t gen_rand_gpr(riscv_reg_t included_reg[] = {},
riscv_reg_t excluded_reg[] = {});
riscv_reg_t gpr;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(gpr,
if (is_compressed) {
gpr inside {[S0:A5]};
}
if (excluded_reg.size() != 0) {
!(gpr inside {excluded_reg});
}
if (included_reg.size() != 0) {
gpr inside {included_reg};
})
int unsigned i;
riscv_reg_t legal_gpr[$];
if (included_reg.size() > 0) begin
legal_gpr = included_reg;
while (is_compressed && (i < legal_gpr.size())) begin
if (legal_gpr[i] < S0 || legal_gpr[i] > A5) begin
legal_gpr.delete(i);
end else begin
i++;
end
end
end else if (is_compressed) begin
legal_gpr = riscv_instr_pkg::compressed_gpr;
end else begin
legal_gpr = riscv_instr_pkg::all_gpr;
end
if (excluded_reg.size() > 0) begin
i = 0;
while (i < legal_gpr.size()) begin
if (legal_gpr[i] inside {excluded_reg}) begin
legal_gpr.delete(i);
end else begin
i++;
end
end
end
`DV_CHECK_FATAL(legal_gpr.size() > 0)
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(i, i < legal_gpr.size();)
gpr = legal_gpr[i];
return gpr;
endfunction
@ -521,6 +544,8 @@ class riscv_instr_base extends uvm_object;
I_FORMAT: // instr rd,rs1,imm
if(instr_name == NOP)
asm_str = "nop";
else if(instr_name == WFI)
asm_str = "wfi";
else if(instr_name == FENCE)
asm_str = $sformatf("fence"); // TODO: Support all fence combinations
else if(instr_name == FENCEI)
@ -602,7 +627,7 @@ class riscv_instr_base extends uvm_object;
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;
ECALL, EBREAK, URET, SRET, MRET, DRET, WFI, SFENCE_VMA : get_opcode = 7'b1110011;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
@ -744,7 +769,7 @@ class riscv_instr_base extends uvm_object;
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;
ECALL, EBREAK, URET, SRET, MRET, DRET, WFI, SFENCE_VMA : get_func3 = 3'b000;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
endcase
endfunction
@ -794,6 +819,7 @@ class riscv_instr_base extends uvm_object;
URET : get_func7 = 7'b0000000;
SRET : get_func7 = 7'b0001000;
MRET : get_func7 = 7'b0011000;
DRET : get_func7 = 7'b0111101;
WFI : get_func7 = 7'b0001000;
SFENCE_VMA: get_func7 = 7'b0001001;
default : `uvm_fatal(`gfn, $sformatf("Unsupported instruction %0s", instr_name.name()))
@ -820,6 +846,8 @@ class riscv_instr_base extends uvm_object;
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 inside {DRET})
binary = $sformatf("%8h", {get_func7(), 5'b10010, 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)

View file

@ -39,9 +39,6 @@ class riscv_instr_gen_config extends uvm_object;
// 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];
@ -52,6 +49,10 @@ class riscv_instr_gen_config extends uvm_object;
// Priviledged mode after boot
rand privileged_mode_t init_privileged_mode;
rand bit[XLEN-1:0] mstatus, mie,
sstatus, sie,
ustatus, uie;
// Key fields in xSTATUS
// Memory protection bits
rand bit mstatus_mprv;
@ -69,12 +70,44 @@ class riscv_instr_gen_config extends uvm_object;
bit check_misa_init_val = 1'b0;
bit check_xstatus = 1'b1;
// Virtual address translation is on for this test
bit virtual_addr_translation_on;
//-----------------------------------------------------------------------------
// User space memory region and stack setting
//-----------------------------------------------------------------------------
mem_region_t mem_region[$] = '{
'{name:"region_0", size_in_bytes: 4096, xwr: 3'b111},
'{name:"region_1", size_in_bytes: 4096 * 4, xwr: 3'b111},
'{name:"region_2", size_in_bytes: 4096 * 2, xwr: 3'b111},
'{name:"region_3", size_in_bytes: 512, xwr: 3'b111},
'{name:"region_4", size_in_bytes: 4096, xwr: 3'b111}
};
// Stack section word length
int stack_len = 5000;
//-----------------------------------------------------------------------------
// Kernel section setting, used by supervisor mode programs
//-----------------------------------------------------------------------------
mem_region_t s_mem_region[$] = '{
'{name:"s_region_0", size_in_bytes: 4096, xwr: 3'b111},
'{name:"s_region_1", size_in_bytes: 4096, xwr: 3'b111}};
// Kernel Stack section word length
int kernel_stack_len = 4000;
// Number of instructions for each kernel program
int kernel_program_instr_cnt = 400;
//-----------------------------------------------------------------------------
// Instruction list based on the config, generate by build_instruction_template
//-----------------------------------------------------------------------------
riscv_instr_base instr_template[riscv_instr_name_t];
riscv_instr_name_t basic_instr[$];
riscv_instr_name_t instr_group[riscv_instr_cateogry_t][$];
riscv_instr_name_t instr_group[riscv_instr_group_t][$];
riscv_instr_name_t instr_category[riscv_instr_cateogry_t][$];
//-----------------------------------------------------------------------------
@ -88,16 +121,16 @@ class riscv_instr_gen_config extends uvm_object;
// 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; // No csr instruction
bit no_ebreak = 1; // No ebreak instruction
bit no_fence; // No fence instruction
bit no_wfi = 1; // No WFI instruction
bit no_branch_jump; // No branch/jump instruction
bit no_load_store; // No load/store instruction
bit no_csr_instr; // No csr instruction
bit no_ebreak = 1; // No ebreak instruction
bit no_dret = 1; // No dret instruction
bit no_fence; // No fence instruction
bit no_wfi = 1; // No WFI instruction
bit enable_unaligned_load_store;
bit enable_illegal_instruction;
bit enable_hint_instruction;
int bin_program_instr_cnt = 200;
int illegal_instr_ratio;
int hint_instr_ratio;
// Directed boot privileged mode, u, m, s
string boot_mode_opts;
int enable_page_table_exception;
@ -106,6 +139,10 @@ class riscv_instr_gen_config extends uvm_object;
string asm_test_suffix;
// Enable interrupt bit in MSTATUS (MIE, SIE, UIE)
int enable_interrupt;
// Generate a bare program without any init/exit/error handling/page table routines
// The generated program can be integrated with a larger program.
// Note that the bare mode program is not expected to run in standalone mode
bit bare_program_mode;
// Enable accessing illegal CSR instruction
// - Accessing non-existence CSR
// - Accessing CSR with wrong privileged mode
@ -123,16 +160,24 @@ class riscv_instr_gen_config extends uvm_object;
bit require_signature_addr = 1'b0;
rand riscv_reg_t signature_addr_reg;
rand riscv_reg_t signature_data_reg;
bit gen_debug_section = 1'b0;
// Register that will be used to handle any DCSR operations inside of the
// debug rom
rand riscv_reg_t scratch_reg;
// Enable a full or empty debug_rom section.
// Full debug_rom will contain random instruction streams.
// Empty debug_rom will contain just dret instruction and will return immediately.
// Will be empty by default.
bit empty_debug_section = 1'b0;
bit gen_debug_section = 1'b0;
// Enable generation of a directed sequence of instructions containing
// ebreak inside the debug_rom.
// Disabled by default.
bit enable_ebreak_in_debug_rom = 1'b0;
// Enable setting dcsr.ebreak(m/s/u)
bit set_dcsr_ebreak = 1'b0;
// Number of sub programs in the debug rom
int num_debug_sub_program = 0;
// Stack space allocated to each program, need to be enough to store necessary context
// Example: RA, SP, T0, loop registers
// Example: RA, SP, T0
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
@ -142,9 +187,6 @@ class riscv_instr_gen_config extends uvm_object;
// 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[];
@ -250,23 +292,24 @@ class riscv_instr_gen_config extends uvm_object;
}
}
constraint reserved_reg_c {
unique {loop_regs};
foreach(loop_regs[i]) {
!(loop_regs[i] inside {default_reserved_regs});
constraint reserve_scratch_reg_c {
scratch_reg != ZERO;
foreach (default_reserved_regs[i]) {
signature_data_reg != default_reserved_regs[i];
signature_addr_reg != default_reserved_regs[i];
}
!(signature_addr_reg inside {ZERO, default_reserved_regs, loop_regs});
!(signature_data_reg inside {ZERO, default_reserved_regs, loop_regs});
signature_addr_reg != signature_data_reg;
}
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;
constraint signature_addr_c {
if (require_signature_addr) {
foreach (default_reserved_regs[i]) {
signature_addr_reg != default_reserved_regs[i];
signature_data_reg != default_reserved_regs[i];
}
signature_data_reg != scratch_reg;
signature_addr_reg != scratch_reg;
signature_data_reg != ZERO;
signature_addr_reg != ZERO;
}
}
@ -287,6 +330,7 @@ class riscv_instr_gen_config extends uvm_object;
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_dret=", no_dret);
get_bool_arg_value("+no_wfi=", no_wfi);
get_bool_arg_value("+no_branch_jump=", no_branch_jump);
get_bool_arg_value("+no_load_store=", no_load_store);
@ -297,9 +341,9 @@ class riscv_instr_gen_config extends uvm_object;
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_int_arg_value("+illegal_instr_ratio=", illegal_instr_ratio);
get_int_arg_value("+hint_instr_ratio=", hint_instr_ratio);
get_bool_arg_value("+enable_unaligned_load_store=", enable_unaligned_load_store);
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);
get_bool_arg_value("+require_signature_addr=", require_signature_addr);
@ -307,7 +351,10 @@ class riscv_instr_gen_config extends uvm_object;
get_hex_arg_value("+signature_addr=", signature_addr);
end
get_bool_arg_value("+gen_debug_section=", gen_debug_section);
get_bool_arg_value("+bare_program_mode=", bare_program_mode);
get_int_arg_value("+num_debug_sub_program=", num_debug_sub_program);
get_bool_arg_value("+enable_ebreak_in_debug_rom=", enable_ebreak_in_debug_rom);
get_bool_arg_value("+set_dcsr_ebreak=", set_dcsr_ebreak);
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)
@ -367,10 +414,8 @@ class riscv_instr_gen_config extends uvm_object;
// 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
default_reserved_regs = {SP, // x2, stack pointer (user stack)
TP // x4, thread pointer, used as kernel stack pointer
};
endfunction
@ -383,11 +428,14 @@ class riscv_instr_gen_config extends uvm_object;
function void post_randomize();
// Setup the list all reserved registers
reserved_regs = {default_reserved_regs, loop_regs};
reserved_regs = {default_reserved_regs, scratch_reg};
// Need to save all loop registers, and RA/T0
min_stack_len_per_program = (max_nested_loop * 2 + 2) * (XLEN/8);
min_stack_len_per_program = 2 * (XLEN/8);
// Check if the setting is legal
check_setting();
if ((init_privileged_mode != MACHINE_MODE) && (SATP_MODE != BARE)) begin
virtual_addr_translation_on = 1'b1;
end
endfunction
function void check_setting();
@ -465,6 +513,15 @@ class riscv_instr_gen_config extends uvm_object;
while (instr_name != instr_name.first);
if (no_ebreak == 0) begin
basic_instr = {basic_instr, EBREAK};
foreach(riscv_instr_pkg::supported_isa[i]) begin
if (riscv_instr_pkg::supported_isa[i] inside {RV32C, RV64C, RV128C, RV32DC, RV32FC}) begin
basic_instr = {basic_instr, C_EBREAK};
break;
end
end
end
if (no_dret == 0) begin
basic_instr = {basic_instr, DRET};
end
if (no_fence == 0) begin
basic_instr = {basic_instr, instr_category[SYNCH]};

View file

@ -14,17 +14,24 @@
* limitations under the License.
*/
`include "dv_defines.svh"
package riscv_instr_pkg;
import uvm_pkg::*;
import riscv_signature_pkg::*;
`include "uvm_macros.svh"
`include "dv_defines.svh"
`include "riscv_defines.svh"
`define include_file(f) `include `"f`"
// Data section setting
typedef struct {
string name;
int unsigned size_in_bytes;
bit [2:0] xwr; // Excutable,Writable,Readale
} mem_region_t;
typedef enum bit [3:0] {
BARE = 4'b0000,
SV32 = 4'b0001,
@ -261,6 +268,7 @@ package riscv_instr_pkg;
AMOMINU_D,
AMOMAXU_D,
// Supervisor instruction
DRET,
MRET,
URET,
SRET,
@ -570,7 +578,8 @@ package riscv_instr_pkg;
TDATA3 = 'h7A3, // Third Debug/Trace trigger data register
DCSR = 'h7B0, // Debug control and status register
DPC = 'h7B1, // Debug PC
DSCRATCH = 'h7B2 // Debug scratch register
DSCRATCH0 = 'h7B2, // Debug scratch register
DSCRATCH1 = 'h7B3 // Debug scratch register
} privileged_reg_t;
typedef enum bit [5:0] {
@ -589,7 +598,6 @@ package riscv_instr_pkg;
} 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
@ -772,10 +780,10 @@ package riscv_instr_pkg;
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)));
// Reserve space from kernel stack to save all 32 GPR except for x0
instr.push_back($sformatf("1: addi sp, sp, -%0d", 31 * (XLEN/8)));
// Push all GPRs to kernel stack
for(int i = 0; i < 32; i++) begin
for(int i = 1; i < 32; i++) begin
instr.push_back($sformatf("%0s x%0d, %0d(sp)", store_instr, i, i * (XLEN/8)));
end
endfunction
@ -787,11 +795,11 @@ package riscv_instr_pkg;
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
for(int i = 1; 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)));
instr.push_back($sformatf("addi sp, sp, %0d", 31 * (XLEN/8)));
if (scratch inside {implemented_csr}) begin
// Move SP to TP
instr.push_back("add tp, sp, zero");
@ -800,6 +808,12 @@ package riscv_instr_pkg;
end
endfunction
riscv_reg_t all_gpr[] = {ZERO, RA, SP, GP, TP, T0, T1, T2, S0, S1, A0,
A1, A2, A3, A4, A5, A6, A7, S2, S3, S4, S5, S6,
S7, S8, S9, S10, S11, T3, T4, T5, T6};
riscv_reg_t compressed_gpr[] = {S0, S1, A0, A1, A2, A3, A4, A5};
`include "riscv_instr_base.sv"
`include "riscv_instr_gen_config.sv"
`include "riscv_illegal_instr.sv"

View file

@ -78,7 +78,8 @@ class riscv_instr_sequence extends uvm_sequence;
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_branch(no_branch), .no_load_store(1'b1));
instr_stream.gen_instr(.no_branch(no_branch), .no_load_store(1'b1),
.is_debug_program(is_debug_program));
if(!is_main_program) begin
gen_stack_enter_instr();
gen_stack_exit_instr();
@ -132,6 +133,8 @@ class riscv_instr_sequence extends uvm_sequence;
virtual function void post_process_instr();
int i;
int label_idx;
int branch_cnt;
int unsigned branch_idx[];
int branch_target[string] = '{default: 0};
// Insert directed instructions, it's randomly mixed with the random instruction stream.
foreach (directed_instr[i]) begin
@ -169,6 +172,11 @@ class riscv_instr_sequence extends uvm_sequence;
end
end
// Generate branch target
branch_idx = new[30];
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(branch_idx,
foreach(branch_idx[i]) {
branch_idx[i] inside {[1:cfg.max_branch_step]};
})
while(i < instr_stream.instr_list.size()) begin
if((instr_stream.instr_list[i].category == BRANCH) &&
(!instr_stream.instr_list[i].branch_assigned) &&
@ -179,11 +187,15 @@ class riscv_instr_sequence extends uvm_sequence;
// 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")
branch_target_label = instr_stream.instr_list[i].idx + branch_idx[branch_cnt];
if (branch_target_label >= label_idx) begin
branch_target_label = label_idx-1;
end
branch_cnt++;
if (branch_cnt == branch_idx.size()) begin
branch_cnt = 0;
branch_idx.shuffle();
end
`uvm_info(get_full_name(),
$sformatf("Processing branch instruction[%0d]:%0s # %0d -> %0d",
i, instr_stream.instr_list[i].convert2asm(),
@ -266,6 +278,7 @@ class riscv_instr_sequence extends uvm_sequence;
str = {prefix, instr_stream.instr_list[i].convert2asm()};
instr_string_list.push_back(str);
end
insert_illegal_hint_instr();
prefix = format_string($sformatf("%0d:", i), LABEL_STR_LEN);
if(!is_main_program) begin
str = {prefix, "ret"};
@ -273,53 +286,36 @@ class riscv_instr_sequence extends uvm_sequence;
end
endfunction
// Convert the instruction stream to binary format
function void generate_binary_stream(ref string binary[$]);
string instr_bin;
string remaining_bin;
function void insert_illegal_hint_instr();
int bin_instr_cnt;
int idx;
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
illegal_instr.init(cfg);
bin_instr_cnt = instr_cnt * cfg.illegal_instr_ratio / 1000;
if (bin_instr_cnt >= 0) begin
`uvm_info(`gfn, $sformatf("Injecting %0d illegal instructions, ratio %0d/100",
bin_instr_cnt, cfg.illegal_instr_ratio), UVM_LOW)
repeat (bin_instr_cnt) begin
`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();
exception != kHintInstr;)
str = {indent, $sformatf(".4byte 0x%s # %0s",
illegal_instr.get_bin_str(), illegal_instr.exception.name())};
idx = $urandom_range(0, instr_string_list.size());
instr_string_list.insert(idx, str);
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});
bin_instr_cnt = instr_cnt * cfg.hint_instr_ratio / 1000;
if (bin_instr_cnt >= 0) begin
`uvm_info(`gfn, $sformatf("Injecting %0d HINT instructions, ratio %0d/100",
bin_instr_cnt, cfg.illegal_instr_ratio), UVM_LOW)
repeat (bin_instr_cnt) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(illegal_instr,
exception == kHintInstr;)
str = {indent, $sformatf(".2byte 0x%s # %0s",
illegal_instr.get_bin_str(), illegal_instr.exception.name())};
idx = $urandom_range(0, instr_string_list.size());
instr_string_list.insert(idx, str);
end
end
endfunction

View file

@ -25,6 +25,9 @@ class riscv_instr_stream extends uvm_object;
string label = "";
// User can specify a small group of available registers to generate various hazard condition
rand riscv_reg_t avail_regs[];
// Some additional reserved registers that should not be used as rd register
// by this instruction stream
riscv_reg_t reserved_rd[];
`uvm_object_utils(riscv_instr_stream)
`uvm_object_new
@ -95,9 +98,17 @@ class riscv_instr_stream extends uvm_object;
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]};
if (idx == 0) begin
instr_list = {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+1:current_instr_cnt-1]};
end
end else begin
instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx:current_instr_cnt-1]};
if (idx == 0) begin
instr_list = {new_instr, instr_list[idx:current_instr_cnt-1]};
end else begin
instr_list = {instr_list[0:idx-1], new_instr, instr_list[idx:current_instr_cnt-1]};
end
end
endfunction
@ -112,10 +123,10 @@ class riscv_instr_stream extends uvm_object;
`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 (insert_instr_position.size() > 0) begin
insert_instr_position.sort();
end
if(contained) begin
insert_instr_position[0] = 0;
if(new_instr_cnt > 1)
@ -144,15 +155,9 @@ endclass
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;
bit kernel_mode;
riscv_instr_name_t allowed_instr[$];
// 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]) {
@ -172,17 +177,7 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
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 setup_allowed_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1);
virtual function void setup_allowed_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1);
allowed_instr = cfg.basic_instr;
if (no_branch == 0) begin
allowed_instr = {allowed_instr, cfg.instr_category[BRANCH]};
@ -192,10 +187,11 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
end
endfunction
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1);
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
bit is_debug_program = 1'b0);
setup_allowed_instr(no_branch, no_load_store);
foreach(instr_list[i]) begin
randomize_instr(instr_list[i]);
randomize_instr(instr_list[i], is_debug_program);
end
// Do not allow branch instruction as the last instruction because there's no
// forward branch target
@ -206,14 +202,23 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
endfunction
function void randomize_instr(riscv_instr_base instr,
bit is_in_debug = 1'b0,
bit skip_rs1 = 1'b0,
bit skip_rs2 = 1'b0,
bit skip_rd = 1'b0,
bit skip_imm = 1'b0,
bit skip_csr = 1'b0);
riscv_instr_name_t instr_name;
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
// if set_dcsr_ebreak is set, we do not want to generate any ebreak
// instructions inside the debug_rom
if (!cfg.enable_ebreak_in_debug_rom && is_in_debug) begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};
!(instr_name inside {EBREAK, C_EBREAK});)
end else begin
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(instr_name,
instr_name inside {allowed_instr};)
end
instr.copy_base_instr(cfg.instr_template[instr_name]);
`uvm_info(`gfn, $sformatf("%s: rs1:%0d, rs2:%0d, rd:%0d, imm:%0d",
instr.instr_name.name(),

View file

@ -16,7 +16,14 @@
// Base class for all load/store instruction stream
class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
class riscv_load_store_base_instr_stream extends riscv_mem_access_stream;
typedef enum bit [1:0] {
NARROW,
HIGH,
MEDIUM,
SPARSE
} locality_e;
rand int unsigned num_load_store;
rand int unsigned num_mixed_instr;
@ -25,6 +32,8 @@ class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
rand int addr[];
rand int unsigned data_page_id;
rand riscv_reg_t rs1_reg;
rand locality_e locality;
rand int max_load_store_offset;
`uvm_object_utils(riscv_load_store_base_instr_stream)
@ -38,13 +47,35 @@ class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
}
constraint addr_c {
solve data_page_id before max_load_store_offset;
solve max_load_store_offset before base;
solve max_load_store_offset before addr;
solve max_load_store_offset before offset;
data_page_id < max_data_page_id;
foreach (data_page[i]) {
if (i == data_page_id) {
max_load_store_offset == data_page[i].size_in_bytes;
}
}
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]};
}
}
constraint offset_locality_c {
solve locality before offset;
foreach(offset[i]) {
if (locality == NARROW) {
soft offset[i] inside {[-16:16]};
} else if (locality == HIGH) {
soft offset[i] inside {[-64:64]};
} else if (locality == MEDIUM) {
soft offset[i] inside {[-256:256]};
} else if (locality == SPARSE) {
soft offset[i] inside {[-2048:2047]};
}
}
}
@ -72,10 +103,10 @@ class riscv_load_store_base_instr_stream extends riscv_directed_instr_stream;
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);
if(kernel_mode) begin
la_instr.imm_str = $sformatf("%s+%0d", cfg.s_mem_region[data_page_id].name, base);
end else begin
la_instr.imm_str = $sformatf("kernel_data_page_%0d+%0d", data_page_id, base);
la_instr.imm_str = $sformatf("%s+%0d", cfg.mem_region[data_page_id].name, base);
end
instr_list.push_front(la_instr);
endfunction
@ -253,27 +284,11 @@ class riscv_load_store_hazard_instr_stream extends riscv_load_store_base_instr_s
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;
class riscv_multi_page_load_store_instr_stream extends riscv_mem_access_stream;
riscv_load_store_stress_instr_stream load_store_instr_stream[];
rand int unsigned num_of_instr_stream;
@ -287,13 +302,17 @@ class riscv_multi_page_load_store_instr_stream extends riscv_directed_instr_stre
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});
}
}
constraint page_c {
solve num_of_instr_stream before data_page_id;
num_of_instr_stream inside {[1 : max_data_page_id]};
unique {data_page_id};
}
// 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 {
@ -334,3 +353,20 @@ class riscv_multi_page_load_store_instr_stream extends riscv_directed_instr_stre
endfunction
endclass
// Access the different locations of the same memory regions
class riscv_mem_region_stress_test extends riscv_multi_page_load_store_instr_stream;
`uvm_object_utils(riscv_mem_region_stress_test)
`uvm_object_new
constraint page_c {
num_of_instr_stream inside {[2:5]};
foreach (data_page_id[i]) {
if (i > 0) {
data_page_id[i] == data_page_id[i-1];
}
}
}
endclass

View file

@ -25,8 +25,8 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
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;
rand riscv_instr_name_t branch_type[];
riscv_instr_base loop_init_instr[];
riscv_instr_base loop_update_instr[];
riscv_instr_base loop_branch_instr[];
@ -34,23 +34,37 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
// Aggregated loop instruction stream
riscv_instr_base loop_instr[];
constraint loop_c {
constraint legal_loop_regs_c {
solve num_of_nested_loop before loop_cnt_reg;
solve num_of_nested_loop before loop_limit_reg;
foreach (loop_cnt_reg[i]) {
loop_cnt_reg[i] != ZERO;
foreach (cfg.default_reserved_regs[j]) {
loop_cnt_reg[i] != cfg.default_reserved_regs[j];
}
}
foreach (loop_limit_reg[i]) {
loop_limit_reg[i] != ZERO;
foreach (cfg.default_reserved_regs[j]) {
loop_limit_reg[i] != cfg.default_reserved_regs[j];
}
}
unique {loop_cnt_reg, loop_limit_reg};
loop_cnt_reg.size() == num_of_nested_loop;
loop_limit_reg.size() == num_of_nested_loop;
}
constraint loop_c {
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;
num_of_nested_loop inside {[1:2]};
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]};
@ -74,13 +88,13 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
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();
reserved_rd = {loop_cnt_reg, loop_limit_reg};
// Generate instructions that mixed with the loop instructions
initialize_instr_list(num_of_instr_in_loop);
gen_instr(1'b1);
@ -113,6 +127,7 @@ class riscv_loop_instr extends riscv_rand_instr_stream;
// 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;
loop_branch_target_instr[i].reserved_rd = reserved_rd;
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_target_instr[i],
!(category inside {LOAD, STORE, BRANCH, JUMP});,
"Cannot randomize branch target instruction")

View file

@ -49,10 +49,9 @@ class riscv_page_table#(satp_mode_t MODE = SV39) extends uvm_object;
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);
instr = {instr,
".align 12",
$sformatf("%0s:", get_name())};
foreach(pte_binary[i]) begin
if (i % 8 == 0) begin
if (XLEN == 64) begin

View file

@ -353,7 +353,8 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// 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("la x%0d, _kernel_instr_start", tmp_reg));
// TODO: Fix kernel instruction/data pages separatedly
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,
@ -460,14 +461,14 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
end
end
end
// ---------------------------------------
// Set the kernel page u bit to 0
// ---------------------------------------
// Load the start and end address of the kernel space
// ---------------------------------------------------------------------------
// Set the kernel page u bit to 0 for supervisor mode instruction/data pages
// ---------------------------------------------------------------------------
if (cfg.support_supervisor_mode) begin
instr = {instr,
"la x20, _kernel_start",
"la x21, _kernel_end",
// Process kernel instruction pages
"la x20, _kernel_instr_start",
"la x21, _kernel_instr_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),
@ -480,7 +481,7 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
"add x20, x22, x20",
"add x21, x22, x21",
$sformatf("li x22, 0x%0x", ubit_mask),
"process_kernel_pte:",
"1:",
// Load the PTE from the memory
$sformatf("l%0s x23, 0(x20)", load_store_unit),
// Unset U bit
@ -490,7 +491,30 @@ class riscv_page_table_list#(satp_mode_t MODE = SV39) extends uvm_object;
// 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"};
"ble x20, x21, 1b",
// Process kernel data pages
"la x20, _kernel_data_start",
// 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)),
// Starting from the first 4KB leaf page table
$sformatf("la x22, page_table_%0d", get_1st_4k_table_id()),
"add x20, x22, x20",
$sformatf("li x22, 0x%0x", ubit_mask),
// Assume 20 PTEs for kernel data pages
$sformatf("addi x20, x20, %0d", 20 * XLEN/8),
"2:",
// 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, 2b"};
end
endfunction

View file

@ -31,29 +31,29 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
MISA: begin
privil_level = M_LEVEL;
add_field("WARL0", 26, WARL);
add_field("WIRI", XLEN-28, WIRI);
add_field("WLRL", XLEN-28, WLRL);
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);
add_field("OFFSET", 7, WPRI);
add_field("BANK", XLEN-7, WPRI);
end
// Machine Architecture ID Register
MARCHID: begin
privil_level = M_LEVEL;
add_field("ARCHITECTURE_ID", XLEN, WIRI);
add_field("ARCHITECTURE_ID", XLEN, WPRI);
end
// Machine Implementation ID Register
MIMPID: begin
privil_level = M_LEVEL;
add_field("IMPLEMENTATION", XLEN, WIRI);
add_field("IMPLEMENTATION", XLEN, WPRI);
end
// Hart ID Register
MHARTID: begin
privil_level = M_LEVEL;
add_field("HART_ID", XLEN, WIRI);
add_field("HART_ID", XLEN, WPRI);
end
// Machine Status Register
MSTATUS: begin
@ -136,7 +136,7 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
privil_level = M_LEVEL;
add_field("USIP", 1, WARL);
add_field("SSIP", 1, WARL);
add_field("WIRI0", 1, WIRI);
add_field("WPRI0", 1, WPRI);
add_field("MSIP", 1, WARL);
add_field("UTIP", 1, WARL);
add_field("STIP", 1, WARL);
@ -144,9 +144,9 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
add_field("MTIP", 1, WARL);
add_field("UEIP", 1, WARL);
add_field("SEIP", 1, WARL);
add_field("WIRI2", 1, WIRI);
add_field("WPRI2", 1, WPRI);
add_field("MEIP", 1, WARL);
add_field("WIRI3", XLEN - 12, WIRI);
add_field("WPRI3", XLEN - 12, WPRI);
end
// Machine interrupt-enable register
MIE: begin
@ -168,27 +168,27 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
// Cycle Count Register
MCYCLE: begin
privil_level = M_LEVEL;
add_field("MCYCLE", 64, WIRI);
add_field("MCYCLE", 64, WPRI);
end
// Instruction Count Register
MINSTRET: begin
privil_level = M_LEVEL;
add_field("MINSTRET", 64, WIRI);
add_field("MINSTRET", 64, WPRI);
end
// Cycle Count Register - RV32I only
MCYCLEH: begin
privil_level = M_LEVEL;
add_field("MCYCLEH", 32, WIRI);
add_field("MCYCLEH", 32, WPRI);
end
// Instruction Count Register - RV32I only
MINSTRETH: begin
privil_level = M_LEVEL;
add_field("MINSTRETH", 32, WIRI);
add_field("MINSTRETH", 32, WPRI);
end
// Hardware Performance Monitor Counters
[MHPMCOUNTER3:MHPMCOUNTER31]: begin
privil_level = M_LEVEL;
add_field($sformatf("%s", reg_name.name()), XLEN, WIRI);
add_field($sformatf("%s", reg_name.name()), XLEN, WARL);
end
// Hardware Performance Monitor Events
[MHPMEVENT3:MHPMEVENT31]: begin
@ -201,7 +201,7 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
`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);
add_field($sformatf("%s", reg_name.name()), 32, WARL);
end
// Machine Counter Enable Register
MCOUNTEREN: begin
@ -324,7 +324,7 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
privil_level = M_LEVEL;
if(XLEN==64) begin
add_field("ADDRESS", 54, WARL);
add_field("WIRI", 10, WIRI);
add_field("WARL", 10, WARL);
end else begin
add_field("ADDRESS", 32, WARL);
end
@ -560,16 +560,6 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t);
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

View file

@ -55,6 +55,9 @@ class riscv_privileged_common_seq extends uvm_sequence;
virtual function void setup_mmode_reg(privileged_mode_t mode, ref riscv_privil_reg regs[$]);
mstatus = riscv_privil_reg::type_id::create("mstatus");
mstatus.init_reg(MSTATUS);
`uvm_info(`gfn, $sformatf("mstatus_val: 0x%0x", cfg.mstatus), UVM_LOW)
mstatus.set_val({cfg.mstatus[XLEN-1:XLEN-21], cfg.mstatus_tvm, cfg.mstatus_mxr,
cfg.mstatus_sum, cfg.mstatus_mprv, cfg.mstatus[16:0]});
`DV_CHECK_RANDOMIZE_FATAL(mstatus, "cannot randomize mstatus");
if(XLEN==64) begin
mstatus.set_field("UXL", 2'b10);
@ -64,10 +67,6 @@ class riscv_privileged_common_seq extends uvm_sequence;
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)
@ -86,12 +85,19 @@ class riscv_privileged_common_seq extends uvm_sequence;
if (MIE inside {implemented_csr}) begin
mie = riscv_privil_reg::type_id::create("mie");
mie.init_reg(MIE);
mie.set_val(cfg.mie);
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);
// TODO(udinator) - since full CSRs are being randomized, it's necessary to hardwire the xTIE
// fields to 1'b0, as it causes some timer interrupts to be triggered in Spike after a certain
// amount of simulation time.
mie.set_field("MTIE", 1'b0);
mie.set_field("STIE", 1'b0);
mie.set_field("UTIE", 1'b0);
regs.push_back(mie);
end
endfunction
@ -100,6 +106,7 @@ class riscv_privileged_common_seq extends uvm_sequence;
sstatus = riscv_privil_reg::type_id::create("sstatus");
sstatus.init_reg(SSTATUS);
`DV_CHECK_RANDOMIZE_FATAL(sstatus, "cannot randomize sstatus")
sstatus.set_val(cfg.sstatus);
sstatus.set_field("SPIE", cfg.enable_interrupt);
sstatus.set_field("SIE", cfg.enable_interrupt);
sstatus.set_field("UPIE", cfg.enable_interrupt);
@ -120,10 +127,13 @@ class riscv_privileged_common_seq extends uvm_sequence;
if (SIE inside {implemented_csr}) begin
sie = riscv_privil_reg::type_id::create("sie");
sie.init_reg(SIE);
sie.set_val(cfg.sie);
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);
sie.set_field("STIE", 1'b0);
sie.set_field("UTIE", 1'b0);
regs.push_back(sie);
end
endfunction
@ -132,14 +142,17 @@ class riscv_privileged_common_seq extends uvm_sequence;
ustatus = riscv_privil_reg::type_id::create("ustatus");
ustatus.init_reg(USTATUS);
`DV_CHECK_RANDOMIZE_FATAL(ustatus, "cannot randomize ustatus")
ustatus.set_val(cfg.ustatus);
ustatus.set_field("UIE", cfg.enable_interrupt);
ustatus.set_field("UPIE", cfg.enable_interrupt);
regs.push_back(ustatus);
if (UIE inside {implemented_csr}) begin
uie = riscv_privil_reg::type_id::create("uie");
uie.init_reg(UIE);
uie.set_val(cfg.uie);
uie.set_field("UEIE", cfg.enable_interrupt);
uie.set_field("USIE", cfg.enable_interrupt);
uie.set_field("UTIE", 1'b0);
regs.push_back(uie);
end
endfunction

View file

@ -76,6 +76,9 @@ class riscv_rand_instr extends riscv_instr_base;
if(cfg.no_wfi) {
instr_name != WFI;
}
if(cfg.no_dret) {
instr_name != DRET;
}
// Below previleged instruction is not generated by default
!(instr_name inside {ECALL, URET, SRET, MRET});
if(cfg.no_load_store) {

View file

@ -153,7 +153,4 @@ class riscv_reg#(type REG_T = privileged_reg_t) extends uvm_object;
end
endfunction
virtual function void set_wiri_wpri_fields();
endfunction
endclass

View file

@ -42,7 +42,10 @@ package riscv_signature_pkg;
IN_SUPERVISOR_MODE,
IN_USER_MODE,
HANDLING_IRQ,
HANDLING_EXCEPTION
FINISHED_IRQ,
HANDLING_EXCEPTION,
ILLEGAL_INSTR_EXCEPTION,
EBREAK_EXCEPTION
} core_status_t;
typedef enum bit {
@ -50,5 +53,4 @@ package riscv_signature_pkg;
TEST_FAIL
} test_result_t;
endpackage

View file

@ -96,7 +96,7 @@ class riscv_instr_base_test extends uvm_test;
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);
test_name = $sformatf("%0s_%0d.S", asm_file_name, i);
apply_directed_instr();
`uvm_info(`gfn, "All directed instruction is applied", UVM_LOW)
cfg.build_instruction_template();

View file

@ -23,8 +23,7 @@ class riscv_rand_instr_test extends riscv_instr_base_test;
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;)
`DV_CHECK_RANDOMIZE_FATAL(cfg)
`uvm_info(`gfn, $sformatf("riscv_instr_gen_config is randomized:\n%0s",
cfg.sprint()), UVM_LOW)
endfunction
@ -35,8 +34,8 @@ class riscv_rand_instr_test extends riscv_instr_base_test;
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);
asm_gen.add_directed_instr_stream("riscv_mem_region_stress_test", 4);
endfunction
endclass

View file

@ -0,0 +1 @@
# Add user macros, routines in this file

View file

@ -25,7 +25,7 @@
--override riscvOVPsim/cpu/PMP_registers=0
--override riscvOVPsim/cpu/simulateexceptions=T
--trace --tracechange --traceshowicount --program <elf>
--finishafter 500000
--finishafter 1000000
- iss: sail
path_var: SAIL_RISCV

View file

@ -76,8 +76,19 @@
+directed_instr_1=riscv_loop_instr,4
+directed_instr_2=riscv_hazard_instr_stream,4
+directed_instr_3=riscv_load_store_hazard_instr_stream,4
+directed_instr_4=riscv_cache_line_stress_instr_stream,4
+directed_instr_5=riscv_multi_page_load_store_instr_stream,4
+directed_instr_4=riscv_multi_page_load_store_instr_stream,4
+directed_instr_5=riscv_mem_region_stress_test,4
rtl_test: core_base_test
- test: riscv_loop_test
description: >
Random instruction stress test
iterations: 2
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=10000
+num_of_sub_program=5
+directed_instr_1=riscv_loop_instr,20
rtl_test: core_base_test
# TODO: Temporarily disable this as compiler seems to generate compressed instruction with rv64im
@ -98,7 +109,7 @@
iterations: 2
gen_test: riscv_instr_base_test
gen_opts: >
+instr_cnt=15000
+instr_cnt=10000
+num_of_sub_program=20
+directed_instr_0=riscv_load_store_rand_instr_stream,8
rtl_test: core_base_test
@ -114,15 +125,16 @@
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,40
+directed_instr_1=riscv_load_store_hazard_instr_stream,40
+directed_instr_2=riscv_cache_line_stress_instr_stream,40
+directed_instr_3=riscv_multi_page_load_store_instr_stream,40
+directed_instr_2=riscv_multi_page_load_store_instr_stream,10
+directed_instr_3=riscv_mem_region_stress_test,10
rtl_test: core_base_test
# TODO: Re-enable this test after all the data/instruction page organization changes are done
- test: riscv_page_table_exception_test
description: >
Test random page table exception handling. An exception handling routine is
designed to fix the page table error and resume execution.
iterations: 2
iterations: 0
gen_test: riscv_rand_instr_test
gen_opts: >
+enable_page_table_exception=1
@ -156,7 +168,7 @@
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+enable_illegal_instruction=1
+illegal_instr_ratio=5
rtl_test: core_base_test
- test: riscv_hint_instr_test
@ -166,7 +178,7 @@
iterations: 2
gen_test: riscv_rand_instr_test
gen_opts: >
+enable_hint_instruction=1
+hint_instr_ratio=5
rtl_test: core_base_test
- test: riscv_ebreak_test
@ -228,8 +240,8 @@
+num_of_sub_program=5
+directed_instr_0=riscv_load_store_rand_instr_stream,20
+directed_instr_1=riscv_load_store_hazard_instr_stream,20
+directed_instr_2=riscv_cache_line_stress_instr_stream,20
+directed_instr_3=riscv_multi_page_load_store_instr_stream,20
+directed_instr_2=riscv_multi_page_load_store_instr_stream,5
+directed_instr_3=riscv_mem_region_stress_test,5
+enable_unaligned_load_store=1
rtl_test: core_ibex_base_test