mirror of
https://github.com/openhwgroup/cve2.git
synced 2025-04-22 13:07:46 -04:00
Update google_riscv-dv to google/riscv-dv@f7e35d7 (#573)
Update code from upstream repository https://github.com/google/riscv- dv to revision f7e35d7939a27ae17b0481eb070e9a36ea335d1f * remove deprecated code (google/riscv-dv#460) (udinator) * Integrate directed C test with yaml flow (google/riscv-dv#455) (Hai Hoang Dang) * Qrun is missing -access=wrc option (google/riscv-dv#457) (Hai Hoang Dang) Signed-off-by: Udi <udij@google.com>
This commit is contained in:
parent
2e258c8521
commit
230c282c36
22 changed files with 104 additions and 4108 deletions
2
vendor/google_riscv-dv.lock.hjson
vendored
2
vendor/google_riscv-dv.lock.hjson
vendored
|
@ -9,6 +9,6 @@
|
|||
upstream:
|
||||
{
|
||||
url: https://github.com/google/riscv-dv
|
||||
rev: a655f34eb5058da442b38ca010b0d008291c11b5
|
||||
rev: f7e35d7939a27ae17b0481eb070e9a36ea335d1f
|
||||
}
|
||||
}
|
||||
|
|
|
@ -179,21 +179,24 @@ real RISC-V processor::
|
|||
run --test=riscv_rand_instr_test --iss=ovpsim,whisper
|
||||
run --test=riscv_rand_instr_test --iss=spike,sail
|
||||
|
||||
Run directed assembly tests
|
||||
Run directed assembly/C tests
|
||||
---------------------------
|
||||
|
||||
Sometimes it might be useful to run some hand-coded assembly tests to hit some
|
||||
Sometimes it might be useful to run some hand-coded assembly/C tests to hit some
|
||||
corner cases::
|
||||
|
||||
|
||||
# Run a single/multiple assembly test
|
||||
# Run a single/multiple assembly/C test
|
||||
run --asm_tests asm_test_path1/asm_test1.S,asm_test_path2/asm_test2.S
|
||||
run --c_tests c_test_path1/c_test1.c,c_test_path2/c_test2.c
|
||||
|
||||
# Run regression with all assembly tests(*.S) under a given directory
|
||||
# Run regression with all assembly tests(*.S)/ C tests(*.c) under a given directory
|
||||
run --asm_tests asm_test_path1,asm_test_path2
|
||||
run --c_tests c_test_path1,c_test_path2
|
||||
|
||||
# Run mix between the assembly test and assembly tests under a directory
|
||||
# Run mix between the assembly/C test and assembly/C tests under a directory
|
||||
run --asm_tests asm_test_path1/asm_test1.S,asm_test_path2
|
||||
run --c_tests c_test_path1/c_test1.c,c_test_path2
|
||||
|
||||
You could also use this approach to integrate the assembly tests
|
||||
You could also use this approach to integrate the assembly/C tests
|
||||
from other sources to riscv-dv flow.
|
||||
|
|
1
vendor/google_riscv-dv/qrun_option.f
vendored
1
vendor/google_riscv-dv/qrun_option.f
vendored
|
@ -1,6 +1,7 @@
|
|||
-64
|
||||
-uvmhome uvm-1.2
|
||||
-sv
|
||||
-access=rwc+/.
|
||||
-mfcu
|
||||
-cuname design_cuname
|
||||
+define+UVM_REGEX_NO_DPI
|
||||
|
|
61
vendor/google_riscv-dv/run.py
vendored
61
vendor/google_riscv-dv/run.py
vendored
|
@ -133,6 +133,7 @@ def get_iss_cmd(base_cmd, elf, log):
|
|||
cmd += (" &> %s" % log)
|
||||
return cmd
|
||||
|
||||
|
||||
def do_compile(compile_cmd, test_list, core_setting_dir, cwd, ext_dir,
|
||||
cmp_opts, output_dir, debug_cmd):
|
||||
"""Compile the instruction generator
|
||||
|
@ -162,6 +163,7 @@ def do_compile(compile_cmd, test_list, core_setting_dir, cwd, ext_dir,
|
|||
logging.debug("Compile command: %s" % cmd)
|
||||
run_cmd(cmd, debug_cmd = debug_cmd)
|
||||
|
||||
|
||||
def run_csr_test(cmd_list, cwd, csr_file, isa, iterations, lsf_cmd,
|
||||
end_signature_addr, timeout_s, output_dir, debug_cmd):
|
||||
"""Run CSR test
|
||||
|
@ -179,6 +181,7 @@ def run_csr_test(cmd_list, cwd, csr_file, isa, iterations, lsf_cmd,
|
|||
else:
|
||||
run_cmd(cmd, timeout_s, debug_cmd = debug_cmd)
|
||||
|
||||
|
||||
def do_simulate(sim_cmd, test_list, cwd, sim_opts, seed_yaml, seed, csr_file,
|
||||
isa, end_signature_addr, lsf_cmd, timeout_s, log_suffix,
|
||||
batch_size, output_dir, verbose, check_return_code, debug_cmd):
|
||||
|
@ -411,6 +414,7 @@ def run_assembly(asm_test, iss_yaml, isa, mabi, gcc_opts, iss_opts, output_dir,
|
|||
if len(iss_list) == 2:
|
||||
compare_iss_log(iss_list, log_list, report)
|
||||
|
||||
|
||||
def run_assembly_from_dir(asm_test_dir, iss_yaml, isa, mabi, gcc_opts, iss,
|
||||
output_dir, setting_dir, debug_cmd):
|
||||
"""Run a directed assembly test from a directory with spike
|
||||
|
@ -440,6 +444,7 @@ def run_assembly_from_dir(asm_test_dir, iss_yaml, isa, mabi, gcc_opts, iss,
|
|||
else:
|
||||
logging.error("No assembly test(*.S) found under %s" % asm_test_dir)
|
||||
|
||||
|
||||
def run_c(c_test, iss_yaml, isa, mabi, gcc_opts, iss_opts, output_dir,
|
||||
setting_dir, debug_cmd):
|
||||
"""Run a directed c test with ISS
|
||||
|
@ -498,9 +503,10 @@ def run_c(c_test, iss_yaml, isa, mabi, gcc_opts, iss_opts, output_dir,
|
|||
if len(iss_list) == 2:
|
||||
compare_iss_log(iss_list, log_list, report)
|
||||
|
||||
|
||||
def run_c_from_dir(c_test_dir, iss_yaml, isa, mabi, gcc_opts, iss,
|
||||
output_dir, setting_dir, debug_cmd):
|
||||
"""Run a directed assembly test from a directory with spike
|
||||
"""Run a directed c test from a directory with spike
|
||||
|
||||
Args:
|
||||
c_test_dir : C test file directory
|
||||
|
@ -527,6 +533,7 @@ def run_c_from_dir(c_test_dir, iss_yaml, isa, mabi, gcc_opts, iss,
|
|||
else:
|
||||
logging.error("No c test(*.c) found under %s" % c_test_dir)
|
||||
|
||||
|
||||
def iss_sim(test_list, output_dir, iss_list, iss_yaml, isa,
|
||||
setting_dir, timeout_s, debug_cmd):
|
||||
"""Run ISS simulation with the generated test program
|
||||
|
@ -593,6 +600,7 @@ def iss_cmp(test_list, iss, output_dir, stop_on_first_error, exp, debug_cmd):
|
|||
compare_iss_log(iss_list, log_list, report, stop_on_first_error, exp)
|
||||
save_regr_report(report)
|
||||
|
||||
|
||||
def compare_iss_log(iss_list, log_list, report, stop_on_first_error=0, exp=False):
|
||||
if (len(iss_list) != 2 or len(log_list) != 2) :
|
||||
logging.error("Only support comparing two ISS logs")
|
||||
|
@ -716,6 +724,7 @@ def setup_parser():
|
|||
help="Generate debug command log file")
|
||||
return parser
|
||||
|
||||
|
||||
def load_config(args, cwd):
|
||||
"""
|
||||
Load configuration from the command line and the configuration file.
|
||||
|
@ -777,6 +786,7 @@ def load_config(args, cwd):
|
|||
cfg = vars(args)
|
||||
return cfg
|
||||
|
||||
|
||||
def main():
|
||||
"""This is the main entry point."""
|
||||
try:
|
||||
|
@ -832,27 +842,38 @@ def main():
|
|||
# Process regression test list
|
||||
matched_list = []
|
||||
# Any tests in the YAML test list that specify a directed assembly test
|
||||
directed_list = []
|
||||
asm_directed_list = []
|
||||
# Any tests in the YAML test list that specify a directed c test
|
||||
c_directed_list = []
|
||||
|
||||
if not args.co:
|
||||
process_regression_list(args.testlist, args.test, args.iterations, matched_list, cwd)
|
||||
for t in list(matched_list):
|
||||
# Check mutual exclusive between gen_test, asm_tests, and c_tests
|
||||
if 'asm_tests' in t:
|
||||
if 'gen_test' in t:
|
||||
logging.error('asm_test must not be defined in the testlist '
|
||||
'together with the gen_test field')
|
||||
if 'gen_test' in t or 'c_tests' in t:
|
||||
logging.error('asm_tests must not be defined in the testlist '
|
||||
'together with the gen_test or c_tests field')
|
||||
sys.exit(RET_FATAL)
|
||||
directed_list.append(t)
|
||||
asm_directed_list.append(t)
|
||||
matched_list.remove(t)
|
||||
|
||||
if len(matched_list) == 0 and len(directed_list) == 0:
|
||||
if 'c_tests' in t:
|
||||
if 'gen_test' in t or 'asm_tests' in t:
|
||||
logging.error('c_tests must not be defined in the testlist '
|
||||
'together with the gen_test or asm_tests field')
|
||||
sys.exit(RET_FATAL)
|
||||
c_directed_list.append(t)
|
||||
matched_list.remove(t)
|
||||
|
||||
if len(matched_list) == 0 and len(asm_directed_list) == 0 and len(c_directed_list) == 0:
|
||||
sys.exit("Cannot find %s in %s" % (args.test, args.testlist))
|
||||
|
||||
# Run instruction generator
|
||||
if args.steps == "all" or re.match(".*gen.*", args.steps):
|
||||
# Run any handcoded/directed assembly tests specified in YAML format
|
||||
if len(directed_list) != 0:
|
||||
for test_entry in directed_list:
|
||||
if len(asm_directed_list) != 0:
|
||||
for test_entry in asm_directed_list:
|
||||
gcc_opts = args.gcc_opts
|
||||
gcc_opts += test_entry.get('gcc_opts', '')
|
||||
path_asm_test = os.path.expanduser(test_entry.get('asm_tests'))
|
||||
|
@ -870,6 +891,28 @@ def main():
|
|||
if not args.debug:
|
||||
logging.error('%s does not exist' % path_asm_test)
|
||||
sys.exit(RET_FAIL)
|
||||
|
||||
# Run any handcoded/directed C tests specified in YAML format
|
||||
if len(c_directed_list) != 0:
|
||||
for test_entry in c_directed_list:
|
||||
gcc_opts = args.gcc_opts
|
||||
gcc_opts += test_entry.get('gcc_opts', '')
|
||||
path_c_test = os.path.expanduser(test_entry.get('c_tests'))
|
||||
if path_c_test:
|
||||
# path_c_test is a directory
|
||||
if os.path.isdir(path_c_test):
|
||||
run_c_from_dir(path_c_test, args.iss_yaml, args.isa, args.mabi,
|
||||
gcc_opts, args.iss, output_dir,
|
||||
args.core_setting_dir, args.debug)
|
||||
# path_c_test is a C file
|
||||
elif os.path.isfile(path_c_test):
|
||||
run_c(path_c_test, args.iss_yaml, args.isa, args.mabi, gcc_opts,
|
||||
args.iss, output_dir, args.core_setting_dir, args.debug)
|
||||
else:
|
||||
if not args.debug:
|
||||
logging.error('%s does not exist' % path_c_test)
|
||||
sys.exit(RET_FAIL)
|
||||
|
||||
# Run remaining tests using the instruction generator
|
||||
gen(matched_list, cfg, output_dir, cwd)
|
||||
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/*
|
||||
* Copyright 2019 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Base class for AMO instruction stream
|
||||
class riscv_amo_base_instr_stream extends riscv_mem_access_stream;
|
||||
|
||||
rand int unsigned num_amo;
|
||||
rand int unsigned num_mixed_instr;
|
||||
rand int base;
|
||||
rand riscv_reg_t rs1_reg;
|
||||
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[];
|
||||
|
||||
`uvm_object_utils(riscv_amo_base_instr_stream)
|
||||
|
||||
constraint rs1_c {
|
||||
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
|
||||
}
|
||||
|
||||
constraint addr_range_c {
|
||||
data_page_id < max_data_page_id;
|
||||
base inside {[0 : max_load_store_offset-1]};
|
||||
}
|
||||
|
||||
constraint aligned_amo_c {
|
||||
if (XLEN == 32) {
|
||||
base % 4 == 0;
|
||||
} else {
|
||||
base % 8 == 0;
|
||||
}
|
||||
}
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
gen_amo_instr();
|
||||
// rs1 cannot be modified by other instructions
|
||||
if(!(rs1_reg inside {reserved_rd})) begin
|
||||
reserved_rd = {reserved_rd, rs1_reg};
|
||||
end
|
||||
add_mixed_instr(num_mixed_instr);
|
||||
add_rs1_init_la_instr(rs1_reg, data_page_id);
|
||||
super.post_randomize();
|
||||
endfunction
|
||||
|
||||
// AMO instruction generation
|
||||
virtual function void gen_amo_instr();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// A pair of LR/SC instruction
|
||||
class riscv_lr_sc_instr_stream extends riscv_amo_base_instr_stream;
|
||||
|
||||
riscv_rand_instr lr_instr;
|
||||
riscv_rand_instr sc_instr;
|
||||
|
||||
constraint legal_c {
|
||||
num_amo == 1;
|
||||
num_mixed_instr inside {[0:15]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_lr_sc_instr_stream)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
lr_instr = riscv_rand_instr::type_id::create("lr_instr");
|
||||
sc_instr = riscv_rand_instr::type_id::create("sc_instr");
|
||||
endfunction
|
||||
|
||||
virtual function void gen_amo_instr();
|
||||
lr_instr.cfg = cfg;
|
||||
sc_instr.cfg = cfg;
|
||||
lr_instr.disable_a_extension_c.constraint_mode(0);
|
||||
sc_instr.disable_a_extension_c.constraint_mode(0);
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(lr_instr,
|
||||
rs1 == rs1_reg;
|
||||
rd != rs1_reg;
|
||||
instr_name inside {LR_W, LR_D};)
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(sc_instr,
|
||||
rs1 == rs1_reg;
|
||||
rd != rs1_reg;
|
||||
instr_name inside {SC_W, SC_D};)
|
||||
instr_list.push_front(lr_instr);
|
||||
instr_list.push_front(sc_instr);
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
class riscv_amo_instr_stream extends riscv_amo_base_instr_stream;
|
||||
|
||||
riscv_rand_instr amo_instr[];
|
||||
|
||||
constraint reasonable_c {
|
||||
solve num_amo before num_mixed_instr;
|
||||
num_amo inside {[1:10]};
|
||||
num_mixed_instr inside {[0:2*num_amo]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_amo_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
virtual function void gen_amo_instr();
|
||||
amo_instr = new[num_amo];
|
||||
foreach (amo_instr[i]) begin
|
||||
amo_instr[i] = riscv_rand_instr::type_id::create($sformatf("amo_instr_%0d", i));
|
||||
amo_instr[i].cfg = cfg;
|
||||
amo_instr[i].disable_a_extension_c.constraint_mode(0);
|
||||
`ifdef DSIM
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
|
||||
rs1 == rs1_reg;
|
||||
rd != rs1_reg;
|
||||
instr_name inside {[AMOSWAP_W:AMOMAXU_D]};)
|
||||
`else
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(amo_instr[i],
|
||||
rs1 == rs1_reg;
|
||||
rd != rs1_reg;
|
||||
category == AMO;)
|
||||
`endif
|
||||
instr_list.push_front(amo_instr[i]);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
|
@ -1,531 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Base class for directed instruction stream
|
||||
class riscv_directed_instr_stream extends riscv_rand_instr_stream;
|
||||
|
||||
`uvm_object_utils(riscv_directed_instr_stream)
|
||||
|
||||
string label;
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
foreach(instr_list[i]) begin
|
||||
instr_list[i].has_label = 1'b0;
|
||||
instr_list[i].atomic = 1'b1;
|
||||
end
|
||||
instr_list[0].comment = $sformatf("Start %0s", get_name());
|
||||
instr_list[$].comment = $sformatf("End %0s", get_name());
|
||||
if(label!= "") begin
|
||||
instr_list[0].label = label;
|
||||
instr_list[0].has_label = 1'b1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// 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[$];
|
||||
|
||||
`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
|
||||
|
||||
// Use "la" instruction to initialize the base regiseter
|
||||
virtual function void add_rs1_init_la_instr(riscv_reg_t gpr, int id, int base = 0);
|
||||
riscv_pseudo_instr la_instr;
|
||||
la_instr = riscv_pseudo_instr::type_id::create("la_instr");
|
||||
la_instr.pseudo_instr_name = LA;
|
||||
la_instr.rd = gpr;
|
||||
if(kernel_mode) begin
|
||||
la_instr.imm_str = $sformatf("%s+%0d", cfg.s_mem_region[id].name, base);
|
||||
end else begin
|
||||
la_instr.imm_str = $sformatf("%s+%0d", cfg.mem_region[id].name, base);
|
||||
end
|
||||
instr_list.push_front(la_instr);
|
||||
endfunction
|
||||
|
||||
// Insert some other instructions to mix with mem_access instruction
|
||||
virtual function void add_mixed_instr(int instr_cnt);
|
||||
riscv_instr_base instr;
|
||||
setup_allowed_instr(1, 1);
|
||||
for(int i = 0; i < instr_cnt; i ++) begin
|
||||
instr = riscv_instr_base::type_id::create("instr");
|
||||
randomize_instr(instr);
|
||||
insert_instr(instr);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Jump instruction (JAL, JALR)
|
||||
// la rd0, jump_tagert_label
|
||||
// addi rd1, offset, rd0
|
||||
// jalr rd, offset, rd1
|
||||
// For JAL, restore the stack before doing the jump
|
||||
class riscv_jump_instr extends riscv_directed_instr_stream;
|
||||
|
||||
riscv_instr_base jump;
|
||||
riscv_instr_base addi;
|
||||
riscv_pseudo_instr la;
|
||||
riscv_instr_base branch;
|
||||
rand riscv_reg_t gpr;
|
||||
rand int imm;
|
||||
rand bit enable_branch;
|
||||
rand int mixed_instr_cnt;
|
||||
riscv_instr_base stack_exit_instr[];
|
||||
string target_program_label;
|
||||
int idx;
|
||||
bit use_jalr;
|
||||
|
||||
constraint instr_c {
|
||||
!(gpr inside {cfg.reserved_regs, ZERO});
|
||||
imm inside {[-1023:1023]};
|
||||
mixed_instr_cnt inside {[5:10]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_jump_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
jump = riscv_instr_base::type_id::create("jump");
|
||||
la = riscv_pseudo_instr::type_id::create("la");
|
||||
addi = riscv_instr_base::type_id::create("addi");
|
||||
branch = riscv_instr_base::type_id::create("branch");
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
riscv_instr_base instr[];
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump,
|
||||
(use_jalr) -> (instr_name == JALR);
|
||||
instr_name dist {JAL := 2, JALR := 6, C_JALR := 2};
|
||||
if (cfg.disable_compressed_instr || (cfg.ra != RA)) {
|
||||
instr_name != C_JALR;
|
||||
}
|
||||
rd == cfg.ra;
|
||||
rs1 == gpr;
|
||||
)
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(addi,
|
||||
rs1 == gpr;
|
||||
instr_name == ADDI;
|
||||
rd == gpr;
|
||||
)
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch,
|
||||
instr_name inside {BEQ, BNE, BLT, BGE, BLTU, BGEU};)
|
||||
la.pseudo_instr_name = LA;
|
||||
la.imm_str = target_program_label;
|
||||
la.rd = gpr;
|
||||
// Generate some random instructions to mix with jump instructions
|
||||
reserved_rd = {gpr};
|
||||
initialize_instr_list(mixed_instr_cnt);
|
||||
gen_instr(1'b1);
|
||||
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.
|
||||
if(enable_branch) instr = {branch};
|
||||
// Restore stack before unconditional jump
|
||||
if(jump.rd == ZERO) begin
|
||||
instr= {stack_exit_instr, instr};
|
||||
end
|
||||
if(jump.instr_name == JAL) begin
|
||||
jump.imm_str = target_program_label;
|
||||
end else if (jump.instr_name == C_JALR) begin
|
||||
instr = {la, instr};
|
||||
end else begin
|
||||
instr = {la, addi, instr};
|
||||
end
|
||||
mix_instr_stream(instr);
|
||||
instr_list = {instr_list, jump};
|
||||
foreach(instr_list[i]) begin
|
||||
instr_list[i].has_label = 1'b0;
|
||||
instr_list[i].atomic = 1'b1;
|
||||
end
|
||||
jump.has_label = 1'b1;
|
||||
jump.label = $sformatf("j_%0s_%0s_%0d", label, target_program_label, idx);
|
||||
branch.imm_str = jump.label;
|
||||
branch.comment = "branch to jump instr";
|
||||
branch.branch_assigned = 1'b1;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Stress back to back jump instruction
|
||||
class riscv_jal_instr extends riscv_rand_instr_stream;
|
||||
|
||||
riscv_instr_base jump[];
|
||||
riscv_instr_base jump_start;
|
||||
riscv_instr_base jump_end;
|
||||
rand int unsigned num_of_jump_instr;
|
||||
riscv_instr_name_t jal[$];
|
||||
|
||||
constraint instr_c {
|
||||
num_of_jump_instr inside {[10:30]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_jal_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
int order[];
|
||||
order = new[num_of_jump_instr];
|
||||
jump = new[num_of_jump_instr];
|
||||
foreach (order[i]) begin
|
||||
order[i] = i;
|
||||
end
|
||||
order.shuffle();
|
||||
setup_allowed_instr(1, 1);
|
||||
jal = {JAL};
|
||||
if (!cfg.disable_compressed_instr) begin
|
||||
jal.push_back(C_J);
|
||||
if (XLEN == 32) begin
|
||||
jal.push_back(C_JAL);
|
||||
end
|
||||
end
|
||||
// First instruction
|
||||
jump_start = riscv_instr_base::type_id::create("jump_start");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_start,
|
||||
instr_name == JAL;
|
||||
rd == cfg.ra;
|
||||
)
|
||||
jump_start.imm_str = $sformatf("%0df", order[0]);
|
||||
jump_start.label = label;
|
||||
// Last instruction
|
||||
jump_end = riscv_instr_base::type_id::create("jump_end");
|
||||
randomize_instr(jump_end);
|
||||
jump_end.label = $sformatf("%0d", num_of_jump_instr);
|
||||
foreach (jump[i]) begin
|
||||
jump[i] = riscv_instr_base::type_id::create($sformatf("jump_%0d", i));
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump[i],
|
||||
instr_name inside {jal};
|
||||
rd dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
|
||||
!(rd inside {cfg.reserved_regs});
|
||||
)
|
||||
jump[i].label = $sformatf("%0d", i);
|
||||
end
|
||||
foreach (order[i]) begin
|
||||
if (i == num_of_jump_instr - 1) begin
|
||||
jump[order[i]].imm_str = $sformatf("%0df", num_of_jump_instr);
|
||||
end else begin
|
||||
if (order[i+1] > order[i]) begin
|
||||
jump[order[i]].imm_str = $sformatf("%0df", order[i+1]);
|
||||
end else begin
|
||||
jump[order[i]].imm_str = $sformatf("%0db", order[i+1]);
|
||||
end
|
||||
end
|
||||
end
|
||||
instr_list = {jump_start, jump, jump_end};
|
||||
foreach (instr_list[i]) begin
|
||||
instr_list[i].has_label = 1'b1;
|
||||
instr_list[i].atomic = 1'b1;
|
||||
end
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Push stack instruction stream
|
||||
class riscv_push_stack_instr extends riscv_rand_instr_stream;
|
||||
|
||||
int stack_len;
|
||||
int num_of_reg_to_save;
|
||||
int num_of_redudant_instr;
|
||||
riscv_instr_base push_stack_instr[];
|
||||
riscv_reg_t saved_regs[];
|
||||
rand riscv_rand_instr branch_instr;
|
||||
rand bit enable_branch;
|
||||
string push_start_label;
|
||||
|
||||
`uvm_object_utils(riscv_push_stack_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
function void init();
|
||||
// Save RA, T0
|
||||
reserved_rd = {cfg.ra};
|
||||
saved_regs = {cfg.ra};
|
||||
num_of_reg_to_save = saved_regs.size();
|
||||
if(num_of_reg_to_save * (XLEN/8) > stack_len) begin
|
||||
`uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
|
||||
stack_len, num_of_reg_to_save))
|
||||
end
|
||||
num_of_redudant_instr = $urandom_range(3,10);
|
||||
initialize_instr_list(num_of_redudant_instr);
|
||||
endfunction
|
||||
|
||||
virtual function void gen_push_stack_instr(int stack_len, bit allow_branch = 1);
|
||||
this.stack_len = stack_len;
|
||||
init();
|
||||
gen_instr(1'b1);
|
||||
push_stack_instr = new[num_of_reg_to_save+1];
|
||||
foreach(push_stack_instr[i]) begin
|
||||
push_stack_instr[i] = riscv_instr_base::type_id::
|
||||
create($sformatf("push_stack_instr_%0d", i));
|
||||
end
|
||||
// addi sp,sp,-imm
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[0],
|
||||
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp;
|
||||
imm == (~stack_len + 1);)
|
||||
push_stack_instr[0].imm_str = $sformatf("-%0d", stack_len);
|
||||
foreach(saved_regs[i]) begin
|
||||
if(XLEN == 32) begin
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
|
||||
instr_name == SW; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
|
||||
end else begin
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(push_stack_instr[i+1],
|
||||
instr_name == SD; rs2 == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
|
||||
end
|
||||
push_stack_instr[i+1].process_load_store = 0;
|
||||
end
|
||||
if (allow_branch) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_FATAL(enable_branch)
|
||||
end else begin
|
||||
enable_branch = 0;
|
||||
end
|
||||
if(enable_branch) begin
|
||||
// Cover jal -> branch scenario, the branch is added before push stack operation
|
||||
branch_instr = riscv_rand_instr::type_id::create("branch_instr");
|
||||
branch_instr.cfg = cfg;
|
||||
`ifdef DSIM
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr,
|
||||
instr_name inside {[BEQ:BGEU], C_BEQZ, C_BNEZ};)
|
||||
`else
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(branch_instr, category == BRANCH;)
|
||||
`endif
|
||||
branch_instr.imm_str = push_start_label;
|
||||
branch_instr.branch_assigned = 1'b1;
|
||||
push_stack_instr[0].label = push_start_label;
|
||||
push_stack_instr[0].has_label = 1'b1;
|
||||
push_stack_instr = {branch_instr, push_stack_instr};
|
||||
end
|
||||
mix_instr_stream(push_stack_instr);
|
||||
foreach(instr_list[i]) begin
|
||||
instr_list[i].atomic = 1'b1;
|
||||
if(instr_list[i].label == "")
|
||||
instr_list[i].has_label = 1'b0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Pop stack instruction stream
|
||||
class riscv_pop_stack_instr extends riscv_rand_instr_stream;
|
||||
|
||||
int stack_len;
|
||||
int num_of_reg_to_save;
|
||||
int num_of_redudant_instr;
|
||||
riscv_instr_base pop_stack_instr[];
|
||||
riscv_reg_t saved_regs[];
|
||||
|
||||
`uvm_object_utils(riscv_pop_stack_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
function void init();
|
||||
reserved_rd = {cfg.ra};
|
||||
num_of_reg_to_save = saved_regs.size();
|
||||
if(num_of_reg_to_save * 4 > stack_len) begin
|
||||
`uvm_fatal(get_full_name(), $sformatf("stack len [%0d] is not enough to store %d regs",
|
||||
stack_len, num_of_reg_to_save))
|
||||
end
|
||||
num_of_redudant_instr = $urandom_range(3,10);
|
||||
initialize_instr_list(num_of_redudant_instr);
|
||||
endfunction
|
||||
|
||||
virtual function void gen_pop_stack_instr(int stack_len, riscv_reg_t saved_regs[]);
|
||||
this.stack_len = stack_len;
|
||||
this.saved_regs = saved_regs;
|
||||
init();
|
||||
gen_instr(1'b1);
|
||||
pop_stack_instr = new[num_of_reg_to_save+1];
|
||||
foreach(pop_stack_instr[i]) begin
|
||||
pop_stack_instr[i] = riscv_instr_base::type_id::
|
||||
create($sformatf("pop_stack_instr_%0d", i));
|
||||
end
|
||||
foreach(saved_regs[i]) begin
|
||||
if(XLEN == 32) begin
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
|
||||
instr_name == LW; rd == saved_regs[i]; rs1 == cfg.sp; imm == 4 * (i+1);)
|
||||
end else begin
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[i],
|
||||
instr_name == LD; rd == saved_regs[i]; rs1 == cfg.sp; imm == 8 * (i+1);)
|
||||
end
|
||||
pop_stack_instr[i].process_load_store = 0;
|
||||
end
|
||||
// addi sp,sp,imm
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(pop_stack_instr[num_of_reg_to_save],
|
||||
instr_name == ADDI; rd == cfg.sp; rs1 == cfg.sp; imm == stack_len;)
|
||||
pop_stack_instr[num_of_reg_to_save].imm_str = $sformatf("%0d", stack_len);
|
||||
mix_instr_stream(pop_stack_instr);
|
||||
foreach(instr_list[i]) begin
|
||||
instr_list[i].atomic = 1'b1;
|
||||
instr_list[i].has_label = 1'b0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Cover the long fprward and backward jump
|
||||
class riscv_long_branch_instr extends riscv_rand_instr_stream;
|
||||
|
||||
int branch_instr_stream_len = 100;
|
||||
int branch_instr_offset = 999;
|
||||
riscv_rand_instr_stream forward_branch_instr_stream;
|
||||
riscv_rand_instr_stream backward_branch_instr_stream;
|
||||
riscv_instr_base jump_instr;
|
||||
|
||||
`uvm_object_utils(riscv_long_branch_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
forward_branch_instr_stream = riscv_rand_instr_stream::type_id::
|
||||
create("forward_branch_instr_stream");
|
||||
backward_branch_instr_stream = riscv_rand_instr_stream::type_id::
|
||||
create("backward_branch_instr_stream");
|
||||
jump_instr = riscv_instr_base::type_id::create("jump_instr");
|
||||
endfunction
|
||||
|
||||
function void init(int instr_len);
|
||||
branch_instr_stream_len = instr_len;
|
||||
initialize_instr_list(branch_instr_offset-branch_instr_stream_len);
|
||||
forward_branch_instr_stream.cfg = cfg;
|
||||
backward_branch_instr_stream.cfg = cfg;
|
||||
forward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
|
||||
backward_branch_instr_stream.initialize_instr_list(branch_instr_stream_len);
|
||||
endfunction
|
||||
|
||||
virtual function void gen_instr(bit no_branch = 1'b0, bit no_load_store = 1'b1,
|
||||
bit is_debug_program = 1'b0);
|
||||
int branch_offset;
|
||||
super.gen_instr(1'b1);
|
||||
forward_branch_instr_stream.gen_instr();
|
||||
backward_branch_instr_stream.gen_instr();
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(jump_instr, instr_name == JAL;)
|
||||
jump_instr.imm_str = "test_done";
|
||||
instr_list = {forward_branch_instr_stream.instr_list, instr_list,
|
||||
jump_instr, backward_branch_instr_stream.instr_list};
|
||||
foreach(instr_list[i]) begin
|
||||
instr_list[i].atomic = 1'b1;
|
||||
if(!instr_list[i].is_branch_target) begin
|
||||
instr_list[i].has_label = 1'b0;
|
||||
end
|
||||
if(instr_list[i].category == BRANCH) begin
|
||||
if(i < branch_instr_stream_len)
|
||||
branch_offset = branch_instr_offset;
|
||||
else
|
||||
branch_offset = -branch_instr_offset;
|
||||
instr_list[i].imm_str = $sformatf("target_%0d", i);
|
||||
instr_list[i].branch_assigned = 1'b1;
|
||||
// Avoid dead loop
|
||||
if(((instr_list[i+branch_offset].category == BRANCH) ||
|
||||
instr_list[i+branch_offset].is_branch_target) && (branch_offset < 0))
|
||||
branch_offset = branch_offset + 1;
|
||||
`uvm_info(get_full_name(), $sformatf("Branch [%0d] %0s -> [%0d] %0s", i,
|
||||
instr_list[i].convert2asm(), i+branch_offset,
|
||||
instr_list[i+branch_offset].convert2asm()), UVM_LOW)
|
||||
if(i < -branch_offset)
|
||||
`uvm_fatal(get_name(), $sformatf("Unexpected branch instr at %0d", i))
|
||||
instr_list[i+branch_offset].label = $sformatf("target_%0d", i);
|
||||
instr_list[i+branch_offset].has_label = 1'b1;
|
||||
instr_list[i+branch_offset].is_branch_target = 1;
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
class riscv_sw_interrupt_instr extends riscv_directed_instr_stream;
|
||||
|
||||
rand bit usip;
|
||||
rand bit ssip;
|
||||
rand bit msip;
|
||||
rand privileged_reg_t ip_reg;
|
||||
rand riscv_pseudo_instr li_instr;
|
||||
rand riscv_instr_base csr_instr;
|
||||
riscv_privil_reg ip;
|
||||
rand riscv_reg_t rs1_reg;
|
||||
|
||||
constraint ip_reg_c {
|
||||
if(cfg.init_privileged_mode == MACHINE_MODE) {
|
||||
ip_reg == MIP;
|
||||
} else {
|
||||
ip_reg == SIP;
|
||||
}
|
||||
(ip_reg == MIP) -> (usip || ssip || msip);
|
||||
(ip_reg == SIP) -> (usip || ssip);
|
||||
}
|
||||
|
||||
constraint instr_c {
|
||||
!(rs1_reg inside {cfg.reserved_regs});
|
||||
rs1_reg != ZERO;
|
||||
li_instr.pseudo_instr_name == LI;
|
||||
li_instr.rd == rs1_reg;
|
||||
csr_instr.instr_name == CSRRW;
|
||||
csr_instr.rs1 == rs1_reg;
|
||||
// TODO: Support non-zero rd for SIP, MIP
|
||||
// csr_instr.rd inside {cfg.avail_regs};
|
||||
csr_instr.rd == ZERO;
|
||||
csr_instr.csr == ip_reg;
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_sw_interrupt_instr)
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
li_instr = riscv_pseudo_instr::type_id::create("li_instr");
|
||||
csr_instr = riscv_instr_base::type_id::create("csr_instr");
|
||||
ip = riscv_privil_reg::type_id::create("ip");
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
// TODO: Support UIP
|
||||
if(cfg.init_privileged_mode == USER_MODE) return;
|
||||
ip.init_reg(ip_reg);
|
||||
if(ip_reg == SIP) begin
|
||||
ip.set_field("USIP", usip);
|
||||
ip.set_field("SSIP", ssip);
|
||||
end else begin
|
||||
ip.set_field("USIP", usip);
|
||||
ip.set_field("SSIP", ssip);
|
||||
ip.set_field("MSIP", msip);
|
||||
end
|
||||
li_instr.imm_str = $sformatf("0x%0x", ip.get_val());
|
||||
csr_instr.comment = ip_reg.name();
|
||||
instr_list = {li_instr, csr_instr};
|
||||
super.post_randomize();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
File diff suppressed because it is too large
Load diff
|
@ -1,295 +0,0 @@
|
|||
// This test read all trace CSV, and collect functional coverage from the instruction trace
|
||||
class riscv_instr_cov_test extends uvm_test;
|
||||
|
||||
typedef uvm_enum_wrapper#(riscv_instr_name_t) instr_enum;
|
||||
typedef uvm_enum_wrapper#(riscv_reg_t) gpr_enum;
|
||||
typedef uvm_enum_wrapper#(privileged_reg_t) preg_enum;
|
||||
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_typedef.sv")
|
||||
|
||||
riscv_instr_gen_config cfg;
|
||||
riscv_instr_cover_group instr_cg;
|
||||
riscv_instr_cov_item instr;
|
||||
string trace_csv[$];
|
||||
string trace[string];
|
||||
int unsigned entry_cnt;
|
||||
int unsigned total_entry_cnt;
|
||||
int unsigned skipped_cnt;
|
||||
int unsigned unexpected_illegal_instr_cnt;
|
||||
|
||||
`uvm_component_utils(riscv_instr_cov_test)
|
||||
`uvm_component_new
|
||||
|
||||
task run_phase(uvm_phase phase);
|
||||
int i;
|
||||
string args;
|
||||
string csv;
|
||||
string line;
|
||||
string header[$];
|
||||
string entry[$];
|
||||
int fd;
|
||||
while(1) begin
|
||||
args = {$sformatf("trace_csv_%0d", i), "=%s"};
|
||||
if ($value$plusargs(args, csv)) begin
|
||||
trace_csv.push_back(csv);
|
||||
end else begin
|
||||
break;
|
||||
end
|
||||
i++;
|
||||
end
|
||||
cfg = riscv_instr_gen_config::type_id::create("cfg");
|
||||
// disable_compressed_instr is not relevant to coverage test
|
||||
cfg.disable_compressed_instr = 0;
|
||||
`ifdef DEPRECATED
|
||||
cfg.build_instruction_template(.skip_instr_exclusion(1));
|
||||
`else
|
||||
riscv_instr::create_instr_list(cfg);
|
||||
`endif
|
||||
instr = riscv_instr_cov_item::type_id::create("instr");
|
||||
instr.rand_mode(0);
|
||||
`ifdef DEPRECATED
|
||||
instr.no_hint_illegal_instr_c.constraint_mode(0);
|
||||
instr.imm_val_c.constraint_mode(0);
|
||||
`endif
|
||||
instr_cg = new(cfg);
|
||||
`uvm_info(`gfn, $sformatf("%0d CSV trace files to be processed", trace_csv.size()), UVM_LOW)
|
||||
foreach (trace_csv[i]) begin
|
||||
bit expect_illegal_instr;
|
||||
entry_cnt = 0;
|
||||
instr_cg.reset();
|
||||
if (uvm_is_match("*illegal*", trace_csv[i])) begin
|
||||
expect_illegal_instr = 1;
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("Processing CSV trace[%0d]: %s", i, trace_csv[i]), UVM_LOW)
|
||||
fd = $fopen(trace_csv[i], "r");
|
||||
if (fd) begin
|
||||
// Get the header line
|
||||
if ($fgets(line, fd)) begin
|
||||
split_string(line, ",", header);
|
||||
`uvm_info(`gfn, $sformatf("Header: %0s", line), UVM_HIGH);
|
||||
end else begin
|
||||
`uvm_info(`gfn, $sformatf("Skipping empty trace file: %0s", trace_csv[i]), UVM_LOW)
|
||||
continue;
|
||||
end
|
||||
while ($fgets(line, fd)) begin
|
||||
split_string(line, ",", entry);
|
||||
if (entry.size() != header.size()) begin
|
||||
`uvm_info(`gfn, $sformatf("Skipping malformed entry[%0d] : %0s", entry_cnt, line), UVM_LOW)
|
||||
skipped_cnt += 1;
|
||||
end else begin
|
||||
trace["csv_entry"] = line;
|
||||
foreach (header[j]) begin
|
||||
trace[header[j]] = entry[j];
|
||||
end
|
||||
post_process_trace();
|
||||
if (trace["instr"] inside {"li", "ret", "la"}) continue;
|
||||
if (uvm_is_match("amo*",trace["instr"]) ||
|
||||
uvm_is_match("lr*" ,trace["instr"]) ||
|
||||
uvm_is_match("sc*" ,trace["instr"])) begin
|
||||
// TODO: Enable functional coverage for AMO test
|
||||
continue;
|
||||
end
|
||||
if (!sample()) begin
|
||||
`uvm_info(`gfn, $sformatf("Found illegal instr: %0s [%0s]",
|
||||
trace["instr"], line), UVM_HIGH)
|
||||
if (!expect_illegal_instr) begin
|
||||
unexpected_illegal_instr_cnt++;
|
||||
end
|
||||
end
|
||||
end
|
||||
entry_cnt += 1;
|
||||
end
|
||||
end else begin
|
||||
`uvm_error(`gfn, $sformatf("%0s cannot be openned", trace_csv[i]))
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("[%s] : %0d instructions processed",
|
||||
trace_csv[i], entry_cnt), UVM_LOW)
|
||||
total_entry_cnt += entry_cnt;
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("Finished processing %0d trace CSV, %0d instructions",
|
||||
trace_csv.size(), total_entry_cnt), UVM_LOW)
|
||||
if ((skipped_cnt > 0) || (unexpected_illegal_instr_cnt > 0)) begin
|
||||
`uvm_error(`gfn, $sformatf("%0d instructions skipped, %0d illegal instruction",
|
||||
skipped_cnt, unexpected_illegal_instr_cnt))
|
||||
|
||||
end else begin
|
||||
`uvm_info(`gfn, "TEST PASSED", UVM_NONE);
|
||||
end
|
||||
endtask
|
||||
|
||||
virtual function void post_process_trace();
|
||||
endfunction
|
||||
|
||||
function void fatal (string str);
|
||||
`uvm_info(`gfn, str, UVM_NONE);
|
||||
if ($test$plusargs("stop_on_first_error")) begin
|
||||
`uvm_fatal(`gfn, "Errors: *. Warnings: * (written by riscv_instr_cov.sv)")
|
||||
end
|
||||
endfunction
|
||||
|
||||
function bit sample();
|
||||
riscv_instr_name_t instr_name;
|
||||
bit [XLEN-1:0] val;
|
||||
if (instr_enum::from_name(process_instr_name(trace["instr"]), instr_name)) begin
|
||||
`ifdef DEPRECATED
|
||||
if (cfg.instr_template.exists(instr_name)) begin
|
||||
instr.copy_base_instr(cfg.instr_template[instr_name]);
|
||||
`else
|
||||
if (riscv_instr::instr_template.exists(instr_name)) begin
|
||||
instr.copy(riscv_instr::instr_template[instr_name]);
|
||||
`endif
|
||||
assign_trace_info_to_instr(instr);
|
||||
instr.pre_sample();
|
||||
instr_cg.sample(instr);
|
||||
return 1'b1;
|
||||
end
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("Cannot find opcode: %0s",
|
||||
process_instr_name(trace["instr"])), UVM_LOW)
|
||||
get_val(trace["binary"], val);
|
||||
if ((val[1:0] != 2'b11) && (RV32C inside {supported_isa})) begin
|
||||
`SAMPLE(instr_cg.compressed_opcode_cg, val[15:0])
|
||||
`SAMPLE(instr_cg.illegal_compressed_instr_cg, val)
|
||||
end
|
||||
if (val[1:0] == 2'b11) begin
|
||||
`uvm_info("DBG", $sformatf("Sample opcode: %0x [%0s]", val[6:2], trace["instr"]), UVM_LOW)
|
||||
`SAMPLE(instr_cg.opcode_cg, val[6:2])
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual function void assign_trace_info_to_instr(riscv_instr_cov_item instr);
|
||||
riscv_reg_t gpr;
|
||||
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr_declares.sv")
|
||||
privileged_reg_t preg;
|
||||
get_val(trace["addr"], instr.pc);
|
||||
get_val(trace["binary"], instr.binary);
|
||||
instr.trace = trace["str"];
|
||||
if (instr.instr_name inside {ECALL, EBREAK, FENCE, FENCE_I, NOP,
|
||||
C_NOP, WFI, MRET, C_EBREAK}) begin
|
||||
return;
|
||||
end
|
||||
if (instr.has_rs2) begin
|
||||
if (get_gpr(trace["rs2"], gpr)) begin
|
||||
instr.rs2 = gpr;
|
||||
get_val(trace["rs2_val"], instr.rs2_value);
|
||||
end else begin
|
||||
`uvm_error(`gfn, $sformatf("Unrecognized rs2: [%0s] (%0s)",
|
||||
trace["rs2"], trace["csv_entry"]))
|
||||
end
|
||||
end
|
||||
if (instr.has_rd) begin
|
||||
if (get_gpr(trace["rd"], gpr)) begin
|
||||
instr.rd = gpr;
|
||||
get_val(trace["rd_val"], instr.rd_value);
|
||||
end else begin
|
||||
`uvm_error(`gfn, $sformatf("Unrecognized rd: [%0s] (%0s)",
|
||||
trace["rd"], trace["csv_entry"]))
|
||||
end
|
||||
end
|
||||
if (instr.has_rs1) begin
|
||||
if (instr.format inside {CI_FORMAT, CR_FORMAT} &&
|
||||
!(instr.instr_name inside {C_JR, C_JALR})) begin
|
||||
instr.rs1 = instr.rd;
|
||||
end else begin
|
||||
if (get_gpr(trace["rs1"], gpr)) begin
|
||||
instr.rs1 = gpr;
|
||||
get_val(trace["rs1_val"], instr.rs1_value);
|
||||
end else begin
|
||||
`uvm_error(`gfn, $sformatf("Unrecognized rs1: [%0s] (%0s)",
|
||||
trace["rs1"], trace["csv_entry"]))
|
||||
end
|
||||
end
|
||||
end
|
||||
if (instr.has_imm) begin
|
||||
get_val(trace["imm"], instr.imm);
|
||||
end
|
||||
if (instr.category == CSR) begin
|
||||
if (preg_enum::from_name(trace["csr"].toupper(), preg)) begin
|
||||
instr.csr = preg;
|
||||
end else begin
|
||||
get_val(trace["csr"], instr.csr);
|
||||
end
|
||||
end
|
||||
if (instr.category inside {LOAD, STORE}) begin
|
||||
if (XLEN == 32) begin
|
||||
instr.mem_addr = instr.rs1_value + instr.imm;
|
||||
end else begin
|
||||
bit [XLEN-32-1:0] padding;
|
||||
if (instr.imm[31]) begin
|
||||
padding = '1;
|
||||
end else begin
|
||||
padding = '0;
|
||||
end
|
||||
instr.mem_addr = instr.rs1_value + {padding, instr.imm};
|
||||
end
|
||||
end
|
||||
|
||||
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr.sv")
|
||||
|
||||
endfunction
|
||||
|
||||
function bit get_gpr(input string str, output riscv_reg_t gpr);
|
||||
str = str.toupper();
|
||||
if (gpr_enum::from_name(str, gpr)) begin
|
||||
return 1'b1;
|
||||
end else begin
|
||||
return 1'b0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
`VECTOR_INCLUDE("riscv_instr_cov_test_inc_assign_trace_info_to_instr_functions.sv")
|
||||
|
||||
function void get_val(input string str, output bit [XLEN-1:0] val);
|
||||
val = str.atohex();
|
||||
endfunction
|
||||
|
||||
function string process_instr_name(string instr_name);
|
||||
instr_name = instr_name.toupper();
|
||||
foreach (instr_name[i]) begin
|
||||
if (instr_name[i] == ".") begin
|
||||
instr_name[i] = "_";
|
||||
end
|
||||
end
|
||||
return instr_name;
|
||||
endfunction
|
||||
|
||||
function void split_string(string str, byte step, ref string result[$]);
|
||||
string tmp_str;
|
||||
int i;
|
||||
bit in_quote;
|
||||
result = {};
|
||||
while (i < str.len()) begin
|
||||
if (str[i] == "\"") begin
|
||||
in_quote = ~in_quote;
|
||||
end else if ((str[i] == step) && !in_quote) begin
|
||||
result.push_back(tmp_str);
|
||||
tmp_str = "";
|
||||
end else begin
|
||||
tmp_str = {tmp_str, str[i]};
|
||||
end
|
||||
if (i == str.len()-1) begin
|
||||
result.push_back(tmp_str);
|
||||
end
|
||||
i++;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void report_phase(uvm_phase phase);
|
||||
uvm_report_server rs;
|
||||
int error_count;
|
||||
|
||||
rs = uvm_report_server::get_server();
|
||||
|
||||
error_count = rs.get_severity_count(UVM_WARNING) +
|
||||
rs.get_severity_count(UVM_ERROR) +
|
||||
rs.get_severity_count(UVM_FATAL);
|
||||
|
||||
if (error_count == 0) begin
|
||||
`uvm_info("", "TEST PASSED", UVM_NONE);
|
||||
end else begin
|
||||
`uvm_info("", "TEST FAILED", UVM_NONE);
|
||||
end
|
||||
`uvm_info("", "TEST GENERATION DONE", UVM_NONE);
|
||||
super.report_phase(phase);
|
||||
endfunction
|
||||
|
||||
endclass
|
|
@ -1,700 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// RISC-V assembly program generator configuration class
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
class riscv_instr_gen_config extends uvm_object;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Random instruction generation settings
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
// Instruction count of the main program
|
||||
rand int main_program_instr_cnt;
|
||||
|
||||
// Instruction count of each sub-program
|
||||
rand int sub_program_instr_cnt[];
|
||||
|
||||
// Instruction count of the debug rom
|
||||
rand int debug_program_instr_cnt;
|
||||
|
||||
// Instruction count of debug sub-programs
|
||||
rand int debug_sub_program_instr_cnt[];
|
||||
|
||||
// Pattern of data section: RAND_DATA, ALL_ZERO, INCR_VAL
|
||||
rand data_pattern_t data_page_pattern;
|
||||
|
||||
// Associate array for delegation configuration for each exception and interrupt
|
||||
// When the bit is 1, the corresponding delegation is enabled.
|
||||
rand bit m_mode_exception_delegation[exception_cause_t];
|
||||
rand bit s_mode_exception_delegation[exception_cause_t];
|
||||
rand bit m_mode_interrupt_delegation[interrupt_cause_t];
|
||||
rand bit s_mode_interrupt_delegation[interrupt_cause_t];
|
||||
|
||||
// Priviledged mode after boot
|
||||
rand privileged_mode_t init_privileged_mode;
|
||||
|
||||
rand bit[XLEN-1:0] mstatus, mie,
|
||||
sstatus, sie,
|
||||
ustatus, uie;
|
||||
|
||||
// Key fields in xSTATUS
|
||||
// Memory protection bits
|
||||
rand bit mstatus_mprv;
|
||||
rand bit mstatus_mxr;
|
||||
rand bit mstatus_sum;
|
||||
rand bit mstatus_tvm;
|
||||
rand bit [1:0] mstatus_fs;
|
||||
rand mtvec_mode_t mtvec_mode;
|
||||
|
||||
// Floating point rounding mode
|
||||
rand f_rounding_mode_t fcsr_rm;
|
||||
|
||||
// Enable sfence.vma instruction
|
||||
rand bit enable_sfence;
|
||||
|
||||
// Reserved register
|
||||
// Reserved for various hardcoded routines
|
||||
rand riscv_reg_t gpr[4];
|
||||
// Used by any DCSR operations inside of the debug rom
|
||||
rand riscv_reg_t scratch_reg;
|
||||
// Use a random register for stack pointer/thread pointer
|
||||
rand riscv_reg_t sp;
|
||||
rand riscv_reg_t tp;
|
||||
rand riscv_reg_t ra;
|
||||
|
||||
// Options for privileged mode CSR checking
|
||||
// Below checking can be made optional as the ISS implementation could be different with the
|
||||
// processor.
|
||||
bit check_misa_init_val = 1'b0;
|
||||
bit check_xstatus = 1'b1;
|
||||
|
||||
// Virtual address translation is on for this test
|
||||
rand 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_group_t][$];
|
||||
riscv_instr_name_t instr_category[riscv_instr_category_t][$];
|
||||
|
||||
// Queue of all the main implemented CSRs that the boot privilege mode cannot access
|
||||
// e.g. these CSRs are in higher privilege modes - access should raise an exception
|
||||
privileged_reg_t invalid_priv_mode_csrs[$];
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Command line options or control knobs
|
||||
//-----------------------------------------------------------------------------
|
||||
// Main options for RISC-V assembly program generation
|
||||
// Number of sub-programs per test
|
||||
int num_of_sub_program = 5;
|
||||
int instr_cnt = 200;
|
||||
int num_of_tests = 1;
|
||||
// For tests doesn't involve load/store, the data section generation could be skipped
|
||||
bit no_data_page;
|
||||
// Options to turn off some specific types of instructions
|
||||
bit no_branch_jump; // No branch/jump instruction
|
||||
bit no_load_store; // No load/store instruction
|
||||
bit no_csr_instr; // 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;
|
||||
int illegal_instr_ratio;
|
||||
int hint_instr_ratio;
|
||||
// Directed boot privileged mode, u, m, s
|
||||
string boot_mode_opts;
|
||||
int enable_page_table_exception;
|
||||
bit no_directed_instr;
|
||||
// A name suffix for the generated assembly program
|
||||
string asm_test_suffix;
|
||||
// Enable interrupt bit in MSTATUS (MIE, SIE, UIE)
|
||||
bit enable_interrupt;
|
||||
// We need a separate control knob for enabling timer interrupts, as Spike
|
||||
// throws an exception if xIE.xTIE is enabled
|
||||
bit enable_timer_irq;
|
||||
// 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
|
||||
bit enable_illegal_csr_instruction;
|
||||
// Enable accessing CSRs at an invalid privilege level
|
||||
bit enable_access_invalid_csr_level;
|
||||
// Enable some dummy writes to main system CSRs (xSTATUS/xIE) at beginning of test
|
||||
// to check repeated writes
|
||||
bit enable_dummy_csr_write;
|
||||
bit randomize_csr = 0;
|
||||
// sfence support
|
||||
bit allow_sfence_exception = 0;
|
||||
// Interrupt/Exception Delegation
|
||||
bit no_delegation = 1;
|
||||
bit force_m_delegation = 0;
|
||||
bit force_s_delegation = 0;
|
||||
bit support_supervisor_mode;
|
||||
bit disable_compressed_instr;
|
||||
// "Memory mapped" address that when written to will indicate some event to
|
||||
// the testbench - testbench will take action based on the value written
|
||||
int signature_addr = 32'hdead_beef;
|
||||
bit require_signature_addr = 1'b0;
|
||||
// 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 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;
|
||||
// Enable debug single stepping
|
||||
bit enable_debug_single_step = 0;
|
||||
// Number of single stepping iterations
|
||||
rand int single_step_iterations;
|
||||
// Enable mstatus.tw bit - causes u-mode WFI to raise illegal instruction exceptions
|
||||
bit set_mstatus_tw;
|
||||
// Stack space allocated to each program, need to be enough to store necessary context
|
||||
// Example: RA, SP, T0
|
||||
int min_stack_len_per_program = 10 * (XLEN/8);
|
||||
int max_stack_len_per_program = 16 * (XLEN/8);
|
||||
// Maximum branch distance, avoid skipping large portion of the code
|
||||
int max_branch_step = 20;
|
||||
// Maximum directed instruction stream sequence count
|
||||
int max_directed_instr_stream_seq = 20;
|
||||
// Reserved registers
|
||||
riscv_reg_t reserved_regs[];
|
||||
// Floating point support
|
||||
bit enable_floating_point;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// Command line options for instruction distribution control
|
||||
//-----------------------------------------------------------------------------
|
||||
int dist_control_mode;
|
||||
int unsigned category_dist[riscv_instr_category_t];
|
||||
|
||||
uvm_cmdline_processor inst;
|
||||
|
||||
constraint default_c {
|
||||
sub_program_instr_cnt.size() == num_of_sub_program;
|
||||
debug_sub_program_instr_cnt.size() == num_debug_sub_program;
|
||||
main_program_instr_cnt inside {[10 : instr_cnt]};
|
||||
foreach(sub_program_instr_cnt[i]) {
|
||||
sub_program_instr_cnt[i] inside {[10 : instr_cnt]};
|
||||
}
|
||||
// Disable sfence if the program is not boot to supervisor mode
|
||||
// If sfence exception is allowed, we can enable sfence instruction in any priviledged mode.
|
||||
// When MSTATUS.TVM is set, executing sfence.vma will be treate as illegal instruction
|
||||
if(allow_sfence_exception) {
|
||||
enable_sfence == 1'b1;
|
||||
(init_privileged_mode != SUPERVISOR_MODE) || (mstatus_tvm == 1'b1);
|
||||
} else {
|
||||
(init_privileged_mode != SUPERVISOR_MODE || !riscv_instr_pkg::support_sfence || mstatus_tvm || no_fence)
|
||||
-> (enable_sfence == 1'b0);
|
||||
}
|
||||
}
|
||||
|
||||
constraint debug_mode_c {
|
||||
if (riscv_instr_pkg::support_debug_mode) {
|
||||
debug_program_instr_cnt inside {[100 : 300]};
|
||||
foreach(debug_sub_program_instr_cnt[i]) {
|
||||
debug_sub_program_instr_cnt[i] inside {[100 : 300]};
|
||||
}
|
||||
}
|
||||
`ifndef DSIM
|
||||
main_program_instr_cnt + sub_program_instr_cnt.sum() == instr_cnt;
|
||||
`else
|
||||
// dsim has some issue supporting sum(), use some approximate constraint to generate
|
||||
// instruction cnt
|
||||
if (num_of_sub_program > 0) {
|
||||
main_program_instr_cnt inside {[10:instr_cnt/2]};
|
||||
foreach (sub_program_instr_cnt[i]) {
|
||||
sub_program_instr_cnt[i] inside {[10:instr_cnt/num_of_sub_program]};
|
||||
}
|
||||
} else {
|
||||
main_program_instr_cnt == instr_cnt;
|
||||
}
|
||||
`endif
|
||||
}
|
||||
|
||||
// Keep the number of single step iterations relatively small
|
||||
constraint debug_single_step_c {
|
||||
if (enable_debug_single_step) {
|
||||
single_step_iterations inside {[10 : 50]};
|
||||
}
|
||||
}
|
||||
|
||||
// Boot privileged mode distribution
|
||||
constraint boot_privileged_mode_dist_c {
|
||||
// Boot to higher privileged mode more often
|
||||
if(riscv_instr_pkg::supported_privileged_mode.size() == 2) {
|
||||
init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 6,
|
||||
riscv_instr_pkg::supported_privileged_mode[1] := 4};
|
||||
} else if (riscv_instr_pkg::supported_privileged_mode.size() == 3) {
|
||||
init_privileged_mode dist {riscv_instr_pkg::supported_privileged_mode[0] := 4,
|
||||
riscv_instr_pkg::supported_privileged_mode[1] := 3,
|
||||
riscv_instr_pkg::supported_privileged_mode[2] := 3};
|
||||
} else {
|
||||
init_privileged_mode == riscv_instr_pkg::supported_privileged_mode[0];
|
||||
}
|
||||
}
|
||||
|
||||
constraint mtvec_c {
|
||||
mtvec_mode inside {supported_interrupt_mode};
|
||||
}
|
||||
|
||||
constraint mstatus_c {
|
||||
// This is default disabled at setup phase. It can be enabled in the exception and interrupt
|
||||
// handling routine
|
||||
mstatus_mprv == 1'b0;
|
||||
}
|
||||
|
||||
// Exception delegation setting
|
||||
constraint exception_delegation_c {
|
||||
// Do not delegate instructino page fault to supervisor/user mode because this may introduce
|
||||
// dead loop. All the subsequent instruction fetches may fail and program cannot recover.
|
||||
m_mode_exception_delegation[INSTRUCTION_PAGE_FAULT] == 1'b0;
|
||||
if(force_m_delegation) {
|
||||
foreach(m_mode_exception_delegation[i]) {
|
||||
soft m_mode_exception_delegation[i] == 1'b1;
|
||||
}
|
||||
foreach(m_mode_interrupt_delegation[i]) {
|
||||
soft m_mode_interrupt_delegation[i] == 1'b1;
|
||||
}
|
||||
}
|
||||
if(force_s_delegation) {
|
||||
foreach(s_mode_exception_delegation[i]) {
|
||||
soft s_mode_exception_delegation[i] == 1'b1;
|
||||
}
|
||||
foreach(s_mode_interrupt_delegation[i]) {
|
||||
soft s_mode_interrupt_delegation[i] == 1'b1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Spike only supports a subset of exception and interrupt delegation
|
||||
// You can modify this constraint if your ISS support different set of delegations
|
||||
constraint delegation_c {
|
||||
foreach(m_mode_exception_delegation[i]) {
|
||||
if(!support_supervisor_mode || no_delegation) {
|
||||
m_mode_exception_delegation[i] == 0;
|
||||
}
|
||||
if(!(i inside {INSTRUCTION_ADDRESS_MISALIGNED, BREAKPOINT, ECALL_UMODE,
|
||||
INSTRUCTION_PAGE_FAULT, LOAD_PAGE_FAULT, STORE_AMO_PAGE_FAULT})) {
|
||||
m_mode_exception_delegation[i] == 0;
|
||||
}
|
||||
}
|
||||
foreach(m_mode_interrupt_delegation[i]) {
|
||||
if(!support_supervisor_mode || no_delegation) {
|
||||
m_mode_interrupt_delegation[i] == 0;
|
||||
}
|
||||
if(!(i inside {S_SOFTWARE_INTR, S_TIMER_INTR, S_EXTERNAL_INTR})) {
|
||||
m_mode_interrupt_delegation[i] == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constraint ra_c {
|
||||
ra dist {RA := 5, T1 := 2, [SP:T0] :/ 1, [T2:T6] :/ 2};
|
||||
ra != sp;
|
||||
ra != tp;
|
||||
ra != ZERO;
|
||||
}
|
||||
|
||||
constraint sp_tp_c {
|
||||
sp != tp;
|
||||
sp dist {SP := 6, RA := 1, [GP:T6] :/ 3};
|
||||
!(sp inside {GP, RA, ZERO});
|
||||
!(tp inside {GP, RA, ZERO});
|
||||
}
|
||||
|
||||
constraint reserve_scratch_reg_c {
|
||||
scratch_reg != ZERO;
|
||||
scratch_reg != sp;
|
||||
scratch_reg != tp;
|
||||
}
|
||||
|
||||
constraint gpr_c {
|
||||
foreach (gpr[i]) {
|
||||
!(gpr[i] inside {sp, tp, scratch_reg, ZERO, RA, GP});
|
||||
}
|
||||
unique {gpr};
|
||||
}
|
||||
|
||||
constraint addr_translaction_c {
|
||||
solve init_privileged_mode before virtual_addr_translation_on;
|
||||
if ((init_privileged_mode != MACHINE_MODE) && (SATP_MODE != BARE)) {
|
||||
virtual_addr_translation_on == 1'b1;
|
||||
} else {
|
||||
virtual_addr_translation_on == 1'b0;
|
||||
}
|
||||
}
|
||||
|
||||
constraint floating_point_c {
|
||||
if (enable_floating_point) {
|
||||
mstatus_fs == 2'b01;
|
||||
} else {
|
||||
mstatus_fs == 2'b00;
|
||||
}
|
||||
}
|
||||
|
||||
`uvm_object_utils_begin(riscv_instr_gen_config)
|
||||
`uvm_field_int(main_program_instr_cnt, UVM_DEFAULT)
|
||||
`uvm_field_sarray_int(sub_program_instr_cnt, UVM_DEFAULT)
|
||||
`uvm_field_int(debug_program_instr_cnt, UVM_DEFAULT)
|
||||
`uvm_field_enum(data_pattern_t, data_page_pattern, UVM_DEFAULT)
|
||||
`uvm_field_enum(privileged_mode_t, init_privileged_mode, UVM_DEFAULT)
|
||||
`uvm_field_array_enum(riscv_reg_t, reserved_regs, UVM_DEFAULT)
|
||||
`uvm_object_utils_end
|
||||
|
||||
function new (string name = "");
|
||||
string s;
|
||||
super.new(name);
|
||||
init_delegation();
|
||||
inst = uvm_cmdline_processor::get_inst();
|
||||
get_int_arg_value("+num_of_tests=", num_of_tests);
|
||||
get_int_arg_value("+enable_page_table_exception=", enable_page_table_exception);
|
||||
get_bool_arg_value("+enable_interrupt=", enable_interrupt);
|
||||
get_bool_arg_value("+enable_timer_irq=", enable_timer_irq);
|
||||
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);
|
||||
get_bool_arg_value("+no_csr_instr=", no_csr_instr);
|
||||
get_bool_arg_value("+enable_illegal_csr_instruction=", enable_illegal_csr_instruction);
|
||||
get_bool_arg_value("+enable_access_invalid_csr_level=", enable_access_invalid_csr_level);
|
||||
get_bool_arg_value("+enable_dummy_csr_write=", enable_dummy_csr_write);
|
||||
get_bool_arg_value("+allow_sfence_exception=", allow_sfence_exception);
|
||||
get_bool_arg_value("+no_data_page=", no_data_page);
|
||||
get_bool_arg_value("+no_directed_instr=", no_directed_instr);
|
||||
get_bool_arg_value("+no_fence=", no_fence);
|
||||
get_bool_arg_value("+no_delegation=", no_delegation);
|
||||
get_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("+force_m_delegation=", force_m_delegation);
|
||||
get_bool_arg_value("+force_s_delegation=", force_s_delegation);
|
||||
get_bool_arg_value("+require_signature_addr=", require_signature_addr);
|
||||
get_bool_arg_value("+disable_compressed_instr=", disable_compressed_instr);
|
||||
get_bool_arg_value("+randomize_csr=", randomize_csr);
|
||||
if (this.require_signature_addr) begin
|
||||
get_hex_arg_value("+signature_addr=", signature_addr);
|
||||
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);
|
||||
get_bool_arg_value("+enable_debug_single_step=", enable_debug_single_step);
|
||||
get_bool_arg_value("+set_mstatus_tw=", set_mstatus_tw);
|
||||
get_bool_arg_value("+enable_floating_point=", enable_floating_point);
|
||||
if(inst.get_arg_value("+boot_mode=", boot_mode_opts)) begin
|
||||
`uvm_info(get_full_name(), $sformatf(
|
||||
"Got boot mode option - %0s", boot_mode_opts), UVM_LOW)
|
||||
case(boot_mode_opts)
|
||||
"m" : init_privileged_mode = MACHINE_MODE;
|
||||
"s" : init_privileged_mode = SUPERVISOR_MODE;
|
||||
"u" : init_privileged_mode = USER_MODE;
|
||||
default: `uvm_fatal(get_full_name(),
|
||||
$sformatf("Illegal boot mode option - %0s", boot_mode_opts))
|
||||
endcase
|
||||
init_privileged_mode.rand_mode(0);
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("riscv_instr_pkg::supported_privileged_mode = %0d",
|
||||
riscv_instr_pkg::supported_privileged_mode.size()), UVM_LOW)
|
||||
void'(inst.get_arg_value("+asm_test_suffix=", asm_test_suffix));
|
||||
// Directed march list from the runtime options, ex. RV32I, RV32M etc.
|
||||
void'(inst.get_arg_value("+march=", s));
|
||||
if(s != "") begin
|
||||
string cmdline_march_list[$];
|
||||
riscv_instr_group_t march;
|
||||
uvm_split_string(s, ",", cmdline_march_list);
|
||||
riscv_instr_pkg::supported_isa.delete();
|
||||
foreach(cmdline_march_list[i]) begin
|
||||
if(uvm_enum_wrapper#(riscv_instr_group_t)::from_name(
|
||||
cmdline_march_list[i].toupper(), march)) begin
|
||||
riscv_instr_pkg::supported_isa.push_back(march);
|
||||
end else begin
|
||||
`uvm_fatal(get_full_name(), $sformatf(
|
||||
"Invalid march %0s specified in command line", cmdline_march_list[i]))
|
||||
end
|
||||
end
|
||||
end
|
||||
if (!(RV32C inside {supported_isa})) begin
|
||||
disable_compressed_instr = 1;
|
||||
end
|
||||
setup_instr_distribution();
|
||||
get_invalid_priv_lvl_csr();
|
||||
endfunction
|
||||
|
||||
function void setup_instr_distribution();
|
||||
string opts;
|
||||
int val;
|
||||
get_int_arg_value("+dist_control_mode=", dist_control_mode);
|
||||
if (dist_control_mode == 1) begin
|
||||
riscv_instr_category_t category;
|
||||
category = category.first;
|
||||
do begin
|
||||
opts = {$sformatf("dist_%0s=", category.name()), "%d"};
|
||||
opts = opts.tolower();
|
||||
if ($value$plusargs(opts, val)) begin
|
||||
category_dist[category] = val;
|
||||
end else begin
|
||||
category_dist[category] = 10; // Default ratio
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("Set dist[%0s] = %0d",
|
||||
category.name(), category_dist[category]), UVM_LOW)
|
||||
category = category.next;
|
||||
end
|
||||
while(category != category.first);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Initialize the exception/interrupt delegation associate array, set all delegation default to 0
|
||||
virtual function void init_delegation();
|
||||
exception_cause_t cause;
|
||||
interrupt_cause_t intr_cause;
|
||||
cause = cause.first;
|
||||
// Init exception delegation array
|
||||
do begin
|
||||
m_mode_exception_delegation[cause] = 1'b0;
|
||||
s_mode_exception_delegation[cause] = 1'b0;
|
||||
cause = cause.next;
|
||||
end
|
||||
while(cause != cause.first);
|
||||
// Init interrupt delegation array
|
||||
intr_cause = intr_cause.first;
|
||||
do begin
|
||||
m_mode_interrupt_delegation[intr_cause] = 1'b0;
|
||||
s_mode_interrupt_delegation[intr_cause] = 1'b0;
|
||||
intr_cause = intr_cause.next;
|
||||
end
|
||||
while(intr_cause != intr_cause.first);
|
||||
endfunction
|
||||
|
||||
function void pre_randomize();
|
||||
foreach (riscv_instr_pkg::supported_privileged_mode[i]) begin
|
||||
if(riscv_instr_pkg::supported_privileged_mode[i] == SUPERVISOR_MODE)
|
||||
support_supervisor_mode = 1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void get_non_reserved_gpr();
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
// Setup the list all reserved registers
|
||||
reserved_regs = {tp, sp, scratch_reg};
|
||||
// Need to save all loop registers, and RA/T0
|
||||
min_stack_len_per_program = 2 * (XLEN/8);
|
||||
// Check if the setting is legal
|
||||
check_setting();
|
||||
// WFI is not supported in umode
|
||||
if (init_privileged_mode == USER_MODE) begin
|
||||
no_wfi = 1'b1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void check_setting();
|
||||
bit support_64b;
|
||||
bit support_128b;
|
||||
foreach (riscv_instr_pkg::supported_isa[i]) begin
|
||||
if (riscv_instr_pkg::supported_isa[i] inside {RV64I, RV64M, RV64A, RV64F, RV64D, RV64C}) begin
|
||||
support_64b = 1'b1;
|
||||
end else if (riscv_instr_pkg::supported_isa[i] inside {RV128I, RV128C}) begin
|
||||
support_128b = 1'b1;
|
||||
end
|
||||
end
|
||||
if (support_128b && XLEN != 128) begin
|
||||
`uvm_fatal(`gfn, "XLEN should be set to 128 based on riscv_instr_pkg::supported_isa setting")
|
||||
end
|
||||
if (!support_128b && support_64b && XLEN != 64) begin
|
||||
`uvm_fatal(`gfn, "XLEN should be set to 64 based on riscv_instr_pkg::supported_isa setting")
|
||||
end
|
||||
if (!(support_128b || support_64b) && XLEN != 32) begin
|
||||
`uvm_fatal(`gfn, "XLEN should be set to 32 based on riscv_instr_pkg::supported_isa setting")
|
||||
end
|
||||
if (!(support_128b || support_64b) && !(SATP_MODE inside {SV32, BARE})) begin
|
||||
`uvm_fatal(`gfn, $sformatf("SATP mode %0s is not supported for RV32G ISA", SATP_MODE.name()))
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Populate invalid_priv_mode_csrs with the main implemented CSRs for each supported privilege mode
|
||||
// TODO(udi) - include performance/pmp/trigger CSRs?
|
||||
virtual function void get_invalid_priv_lvl_csr();
|
||||
string invalid_lvl[$];
|
||||
string csr_name;
|
||||
privileged_reg_t csr;
|
||||
// Debug CSRs are inaccessible from all but Debug Mode, and we cannot boot into Debug Mode
|
||||
invalid_lvl.push_back("D");
|
||||
case (init_privileged_mode)
|
||||
MACHINE_MODE: begin
|
||||
end
|
||||
SUPERVISOR_MODE: begin
|
||||
invalid_lvl.push_back("M");
|
||||
end
|
||||
USER_MODE: begin
|
||||
invalid_lvl.push_back("S");
|
||||
invalid_lvl.push_back("M");
|
||||
end
|
||||
default: begin
|
||||
`uvm_fatal(`gfn, "Unsupported initialization privilege mode")
|
||||
end
|
||||
endcase
|
||||
foreach (implemented_csr[i]) begin
|
||||
privileged_reg_t csr = implemented_csr[i];
|
||||
csr_name = csr.name();
|
||||
if (csr_name[0] inside {invalid_lvl}) begin
|
||||
invalid_priv_mode_csrs.push_back(implemented_csr[i]);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get an integer argument from comand line
|
||||
function void get_int_arg_value(string cmdline_str, ref int val);
|
||||
string s;
|
||||
if(inst.get_arg_value(cmdline_str, s)) begin
|
||||
val = s.atoi();
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get a bool argument from comand line
|
||||
function void get_bool_arg_value(string cmdline_str, ref bit val);
|
||||
string s;
|
||||
if(inst.get_arg_value(cmdline_str, s)) begin
|
||||
val = s.atobin();
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Get a hex argument from command line
|
||||
function void get_hex_arg_value(string cmdline_str, ref int val);
|
||||
string s;
|
||||
if(inst.get_arg_value(cmdline_str, s)) begin
|
||||
val = s.atohex();
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Build instruction template
|
||||
virtual function void build_instruction_template(bit skip_instr_exclusion = 0);
|
||||
riscv_instr_name_t instr_name;
|
||||
riscv_instr_name_t excluded_instr[$];
|
||||
excluded_instr = {INVALID_INSTR};
|
||||
if (!skip_instr_exclusion) begin
|
||||
get_excluded_instr(excluded_instr);
|
||||
end
|
||||
instr_name = instr_name.first;
|
||||
do begin
|
||||
riscv_instr_base instr;
|
||||
if (!(instr_name inside {unsupported_instr, excluded_instr})) begin
|
||||
instr = riscv_instr_base::type_id::create("instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(instr, instr_name == local::instr_name;)
|
||||
if ((instr.group inside {supported_isa}) &&
|
||||
!(disable_compressed_instr && instr.is_compressed) &&
|
||||
!(!enable_floating_point && (instr.group inside {RV32F, RV64F, RV32D, RV64D}))) begin
|
||||
`uvm_info(`gfn, $sformatf("Adding [%s] %s to the list",
|
||||
instr.group.name(), instr.instr_name.name()), UVM_HIGH)
|
||||
instr_group[instr.group].push_back(instr_name);
|
||||
instr_category[instr.category].push_back(instr_name);
|
||||
instr_template[instr_name] = instr;
|
||||
end
|
||||
end
|
||||
instr_name = instr_name.next;
|
||||
end
|
||||
while (instr_name != instr_name.first);
|
||||
endfunction
|
||||
|
||||
virtual function void build_instruction_list();
|
||||
basic_instr = {instr_category[SHIFT], instr_category[ARITHMETIC],
|
||||
instr_category[LOGICAL], instr_category[COMPARE]};
|
||||
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
|
||||
if (no_dret == 0) begin
|
||||
basic_instr = {basic_instr, DRET};
|
||||
end
|
||||
if (no_fence == 0) begin
|
||||
basic_instr = {basic_instr, instr_category[SYNCH]};
|
||||
end
|
||||
// TODO: Support CSR instruction in other mode
|
||||
if ((no_csr_instr == 0) && (init_privileged_mode == MACHINE_MODE)) begin
|
||||
`uvm_info(`gfn, $sformatf("Adding CSR instr, mode: %0s", init_privileged_mode.name()), UVM_LOW)
|
||||
basic_instr = {basic_instr, instr_category[CSR]};
|
||||
end
|
||||
if (no_wfi == 0) begin
|
||||
basic_instr = {basic_instr, WFI};
|
||||
end
|
||||
endfunction
|
||||
|
||||
virtual function void get_excluded_instr(ref riscv_instr_name_t excluded[$]);
|
||||
// Below instrutions will modify stack pointer, not allowed in normal instruction stream.
|
||||
// It can be used in stack operation instruction stream.
|
||||
excluded = {excluded, C_SWSP, C_SDSP, C_ADDI16SP};
|
||||
if (!enable_sfence) begin
|
||||
excluded = {excluded, SFENCE_VMA};
|
||||
end
|
||||
if (no_fence) begin
|
||||
excluded = {excluded, FENCE, FENCE_I, SFENCE_VMA};
|
||||
end
|
||||
// TODO: Support C_ADDI4SPN
|
||||
excluded = {excluded, C_ADDI4SPN};
|
||||
endfunction
|
||||
|
||||
endclass
|
|
@ -1,306 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Base class for RISC-V instruction stream
|
||||
// A instruction stream here is a queue of RISC-V basic instructions.
|
||||
// This class also provides some functions to manipulate the instruction stream, like insert a new
|
||||
// instruction, mix two instruction streams etc.
|
||||
class riscv_instr_stream extends uvm_object;
|
||||
|
||||
`INSTR instr_list[$];
|
||||
int unsigned instr_cnt;
|
||||
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
|
||||
|
||||
// Initialize the instruction stream, create each instruction instance
|
||||
function void initialize_instr_list(int unsigned instr_cnt);
|
||||
instr_list = {};
|
||||
this.instr_cnt = instr_cnt;
|
||||
create_instr_instance();
|
||||
endfunction
|
||||
|
||||
virtual function void create_instr_instance();
|
||||
`INSTR instr;
|
||||
for(int i = 0; i < instr_cnt; i++) begin
|
||||
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
|
||||
instr_list.push_back(instr);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Insert an instruction to the existing instruction stream at the given index
|
||||
// When index is -1, the instruction is injected at a random location
|
||||
function void insert_instr(`INSTR instr, int idx = -1);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
if(idx == -1) begin
|
||||
idx = $urandom_range(0, current_instr_cnt-1);
|
||||
while(instr_list[idx].atomic) begin
|
||||
idx += 1;
|
||||
if (idx == current_instr_cnt - 1) begin
|
||||
instr_list = {instr_list, instr};
|
||||
return;
|
||||
end
|
||||
end
|
||||
end else if((idx > current_instr_cnt) || (idx < 0)) begin
|
||||
`uvm_error(`gfn, $sformatf("Cannot insert instr:%0s at idx %0d",
|
||||
instr.convert2asm(), idx))
|
||||
end
|
||||
instr_list.insert(idx, instr);
|
||||
endfunction
|
||||
|
||||
// Insert an instruction to the existing instruction stream at the given index
|
||||
// When index is -1, the instruction is injected at a random location
|
||||
// When replace is 1, the original instruction at the inserted position will be replaced
|
||||
function void insert_instr_stream(`INSTR new_instr[], int idx = -1, bit replace = 1'b0);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
int new_instr_cnt = new_instr.size();
|
||||
if(current_instr_cnt == 0) begin
|
||||
instr_list = new_instr;
|
||||
return;
|
||||
end
|
||||
if(idx == -1) begin
|
||||
idx = $urandom_range(0, current_instr_cnt-1);
|
||||
repeat(10) begin
|
||||
if (instr_list[idx].atomic) break;
|
||||
idx = $urandom_range(0, current_instr_cnt-1);
|
||||
end
|
||||
if (instr_list[idx].atomic) begin
|
||||
foreach (instr_list[i]) begin
|
||||
if (!instr_list[i].atomic) begin
|
||||
idx = i;
|
||||
break;
|
||||
end
|
||||
end
|
||||
if (instr_list[idx].atomic) begin
|
||||
`uvm_fatal(`gfn, $sformatf("Cannot inject the instruction"))
|
||||
end
|
||||
end
|
||||
end else if((idx > current_instr_cnt) || (idx < 0)) begin
|
||||
`uvm_error(`gfn, $sformatf("Cannot insert instr stream at idx %0d", idx))
|
||||
end
|
||||
// When replace is 1, the original instruction at this index will be removed. The label of the
|
||||
// original instruction will be copied to the head of inserted instruction stream.
|
||||
if(replace) begin
|
||||
new_instr[0].label = instr_list[idx].label;
|
||||
new_instr[0].has_label = instr_list[idx].has_label;
|
||||
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
|
||||
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
|
||||
|
||||
// Mix the input instruction stream with the original instruction, the instruction order is
|
||||
// preserved. When 'contained' is set, the original instruction stream will be inside the
|
||||
// new instruction stream with the first and last instruction from the input instruction stream.
|
||||
function void mix_instr_stream(`INSTR new_instr[], bit contained = 1'b0);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
int insert_instr_position[];
|
||||
int new_instr_cnt = new_instr.size();
|
||||
insert_instr_position = new[new_instr_cnt];
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(insert_instr_position,
|
||||
foreach(insert_instr_position[i]) {
|
||||
insert_instr_position[i] inside {[0:current_instr_cnt-1]};
|
||||
})
|
||||
if (insert_instr_position.size() > 0) begin
|
||||
insert_instr_position.sort();
|
||||
end
|
||||
if(contained) begin
|
||||
insert_instr_position[0] = 0;
|
||||
if(new_instr_cnt > 1)
|
||||
insert_instr_position[new_instr_cnt-1] = current_instr_cnt-1;
|
||||
end
|
||||
foreach(new_instr[i]) begin
|
||||
insert_instr(new_instr[i], insert_instr_position[i] + i);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function string convert2string();
|
||||
string str;
|
||||
foreach(instr_list[i])
|
||||
str = {str, instr_list[i].convert2asm(), "\n"};
|
||||
return str;
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Generate a random instruction stream based on the configuration
|
||||
// There are two ways to use this class to generate instruction stream
|
||||
// 1. For short instruction stream, you can call randomize() directly.
|
||||
// 2. For long instruction stream (>1K), randomize() all instructions together might take a long
|
||||
// time for the constraint solver. In this case, you can call gen_instr to generate instructions
|
||||
// one by one. The time only grows linearly with the instruction count
|
||||
class riscv_rand_instr_stream extends riscv_instr_stream;
|
||||
|
||||
riscv_instr_gen_config cfg;
|
||||
bit kernel_mode;
|
||||
riscv_instr_name_t allowed_instr[$];
|
||||
int unsigned category_dist[riscv_instr_category_t];
|
||||
|
||||
`uvm_object_utils(riscv_rand_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
virtual function void create_instr_instance();
|
||||
`INSTR instr;
|
||||
for (int i = 0; i < instr_cnt; i++) begin
|
||||
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
|
||||
instr_list.push_back(instr);
|
||||
end
|
||||
endfunction
|
||||
|
||||
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]};
|
||||
end
|
||||
if (no_load_store == 0) begin
|
||||
allowed_instr = {allowed_instr, cfg.instr_category[LOAD], cfg.instr_category[STORE]};
|
||||
end
|
||||
setup_instruction_dist(no_branch, no_load_store);
|
||||
endfunction
|
||||
|
||||
function void setup_instruction_dist(bit no_branch = 1'b0, bit no_load_store = 1'b1);
|
||||
if (cfg.dist_control_mode) begin
|
||||
category_dist = cfg.category_dist;
|
||||
if (no_branch) begin
|
||||
category_dist[BRANCH] = 0;
|
||||
end
|
||||
if (no_load_store) begin
|
||||
category_dist[LOAD] = 0;
|
||||
category_dist[STORE] = 0;
|
||||
end
|
||||
`uvm_info(`gfn, $sformatf("setup_instruction_dist: %0d", category_dist.size()), UVM_LOW)
|
||||
end
|
||||
endfunction
|
||||
|
||||
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], is_debug_program);
|
||||
end
|
||||
// Do not allow branch instruction as the last instruction because there's no
|
||||
// forward branch target
|
||||
while (instr_list[$].category == BRANCH) begin
|
||||
void'(instr_list.pop_back());
|
||||
if (instr_list.size() == 0) break;
|
||||
end
|
||||
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,
|
||||
bit disable_dist = 1'b0);
|
||||
riscv_instr_name_t instr_name;
|
||||
if ((cfg.dist_control_mode == 1) && !disable_dist) begin
|
||||
riscv_instr_category_t category;
|
||||
int unsigned idx;
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(category,
|
||||
category dist {LOAD := category_dist[LOAD],
|
||||
STORE := category_dist[STORE],
|
||||
SHIFT := category_dist[SHIFT],
|
||||
ARITHMETIC := category_dist[ARITHMETIC],
|
||||
LOGICAL := category_dist[LOGICAL],
|
||||
COMPARE := category_dist[COMPARE],
|
||||
BRANCH := category_dist[BRANCH],
|
||||
SYNCH := category_dist[SYNCH],
|
||||
CSR := category_dist[CSR]};)
|
||||
idx = $urandom_range(0, cfg.instr_category[category].size() - 1);
|
||||
instr_name = cfg.instr_category[category][idx];
|
||||
// if set_dcsr_ebreak is set, we do not want to generate any ebreak
|
||||
// instructions inside the debug_rom
|
||||
end else if ((cfg.no_ebreak && !is_in_debug) ||
|
||||
(!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(),
|
||||
instr.has_rs1,
|
||||
instr.has_rs2,
|
||||
instr.has_rd,
|
||||
instr.has_imm), UVM_FULL)
|
||||
if (instr.has_imm && !skip_imm) begin
|
||||
instr.gen_rand_imm();
|
||||
end
|
||||
if (instr.has_rs1 && !skip_rs1) begin
|
||||
if (instr.is_compressed) begin
|
||||
// Compressed instruction could use the same register for rs1 and rd
|
||||
instr.rs1 = instr.gen_rand_gpr(
|
||||
.included_reg(avail_regs),
|
||||
.excluded_reg({reserved_rd, cfg.reserved_regs}));
|
||||
end else begin
|
||||
instr.rs1 = instr.gen_rand_gpr(.included_reg(avail_regs));
|
||||
end
|
||||
end
|
||||
if (instr.has_rs2 && !skip_rs2) begin
|
||||
instr.rs2 = instr.gen_rand_gpr(.included_reg(avail_regs));
|
||||
end
|
||||
if (instr.has_rd && !skip_rd) begin
|
||||
if (instr_name == C_LUI) begin
|
||||
instr.rd = instr.gen_rand_gpr(
|
||||
.included_reg(avail_regs),
|
||||
.excluded_reg({reserved_rd, cfg.reserved_regs, SP}));
|
||||
end else begin
|
||||
instr.rd = instr.gen_rand_gpr(
|
||||
.included_reg(avail_regs),
|
||||
.excluded_reg({reserved_rd, cfg.reserved_regs}));
|
||||
end
|
||||
end
|
||||
if ((instr.category == CSR) && !skip_csr) begin
|
||||
instr.gen_rand_csr(.privileged_mode(cfg.init_privileged_mode),
|
||||
.enable_floating_point(cfg.enable_floating_point),
|
||||
.illegal_csr_instr(cfg.enable_illegal_csr_instruction),
|
||||
.legal_invalid_csr_instr(cfg.enable_access_invalid_csr_level),
|
||||
.invalid_csrs(cfg.invalid_priv_mode_csrs));
|
||||
end
|
||||
if (instr.has_fs1) begin
|
||||
instr.fs1 = instr.gen_rand_fpr();
|
||||
end
|
||||
if (instr.has_fs2) begin
|
||||
instr.fs2 = instr.gen_rand_fpr();
|
||||
end
|
||||
if (instr.has_fs3) begin
|
||||
instr.fs3 = instr.gen_rand_fpr();
|
||||
end
|
||||
if (instr.has_fd) begin
|
||||
instr.fd = instr.gen_rand_fpr();
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
|
@ -1,454 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Base class for all load/store instruction stream
|
||||
|
||||
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;
|
||||
rand int base;
|
||||
int offset[];
|
||||
int addr[];
|
||||
riscv_instr_base load_store_instr[$];
|
||||
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)
|
||||
|
||||
constraint rs1_c {
|
||||
!(rs1_reg inside {cfg.reserved_regs, reserved_rd, ZERO});
|
||||
}
|
||||
|
||||
constraint addr_c {
|
||||
solve data_page_id before max_load_store_offset;
|
||||
solve max_load_store_offset before base;
|
||||
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]};
|
||||
}
|
||||
|
||||
function new(string name = "");
|
||||
super.new(name);
|
||||
endfunction
|
||||
|
||||
virtual function void randomize_offset();
|
||||
int offset_, addr_;
|
||||
offset = new[num_load_store];
|
||||
addr = new[num_load_store];
|
||||
for (int i=0; i<num_load_store; i++) begin
|
||||
if (!std::randomize(offset_, addr_) with {
|
||||
// Locality
|
||||
if (locality == NARROW) {
|
||||
soft offset_ inside {[-16:16]};
|
||||
} else if (locality == HIGH) {
|
||||
soft offset_ inside {[-64:64]};
|
||||
} else if (locality == MEDIUM) {
|
||||
soft offset_ inside {[-256:256]};
|
||||
} else if (locality == SPARSE) {
|
||||
soft offset_ inside {[-2048:2047]};
|
||||
}
|
||||
addr_ == base + offset_;
|
||||
addr_ inside {[0 : max_load_store_offset - 1]};
|
||||
}) begin
|
||||
`uvm_fatal(`gfn, "Cannot randomize load/store offset")
|
||||
end
|
||||
offset[i] = offset_;
|
||||
addr[i] = addr_;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void post_randomize();
|
||||
randomize_offset();
|
||||
// rs1 cannot be modified by other instructions
|
||||
if(!(rs1_reg inside {reserved_rd})) begin
|
||||
reserved_rd = {reserved_rd, rs1_reg};
|
||||
end
|
||||
gen_load_store_instr();
|
||||
add_mixed_instr(num_mixed_instr);
|
||||
add_rs1_init_la_instr(rs1_reg, data_page_id, base);
|
||||
super.post_randomize();
|
||||
endfunction
|
||||
|
||||
// Generate each load/store instruction
|
||||
virtual function void gen_load_store_instr();
|
||||
bit enable_compressed_load_store;
|
||||
riscv_instr_base instr;
|
||||
if(avail_regs.size() > 0) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(avail_regs,
|
||||
unique{avail_regs};
|
||||
avail_regs[0] inside {[S0 : A5]};
|
||||
foreach(avail_regs[i]) {
|
||||
!(avail_regs[i] inside {cfg.reserved_regs, reserved_rd});
|
||||
},
|
||||
"Cannot randomize avail_regs")
|
||||
end
|
||||
if ((rs1_reg inside {[S0 : A5]}) && !cfg.disable_compressed_instr) begin
|
||||
enable_compressed_load_store = 1;
|
||||
end
|
||||
foreach(addr[i]) begin
|
||||
instr = riscv_instr_base::type_id::create("instr");
|
||||
// Assign the allowed load/store instructions based on address alignment
|
||||
// This is done separately rather than a constraint to improve the randomization performance
|
||||
allowed_instr = {LB, LBU, SB};
|
||||
if (!cfg.enable_unaligned_load_store) begin
|
||||
if (addr[i][0] == 1'b0) begin
|
||||
allowed_instr = {LH, LHU, SH, allowed_instr};
|
||||
end
|
||||
if (addr[i] % 4 == 0) begin
|
||||
allowed_instr = {LW, SW, allowed_instr};
|
||||
if (cfg.enable_floating_point) begin
|
||||
allowed_instr = {FLW, FSW, allowed_instr};
|
||||
end
|
||||
if((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
|
||||
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
|
||||
enable_compressed_load_store) begin
|
||||
allowed_instr = {C_LW, C_SW, allowed_instr};
|
||||
if (cfg.enable_floating_point && (RV32FC inside {supported_isa})) begin
|
||||
allowed_instr = {C_FLW, C_FSW, allowed_instr};
|
||||
end
|
||||
end
|
||||
end
|
||||
if ((XLEN >= 64) && (addr[i] % 8 == 0)) begin
|
||||
allowed_instr = {LWU, LD, SD, allowed_instr};
|
||||
if (cfg.enable_floating_point && (RV32D inside {supported_isa})) begin
|
||||
allowed_instr = {FLD, FSD, allowed_instr};
|
||||
end
|
||||
if((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
|
||||
(RV64C inside {riscv_instr_pkg::supported_isa} &&
|
||||
enable_compressed_load_store)) begin
|
||||
allowed_instr = {C_LD, C_SD, allowed_instr};
|
||||
if (cfg.enable_floating_point && (RV32DC inside {supported_isa})) begin
|
||||
allowed_instr = {C_FLD, C_FSD, allowed_instr};
|
||||
end
|
||||
end
|
||||
end
|
||||
end else begin
|
||||
allowed_instr = {LW, SW, LH, LHU, SH, allowed_instr};
|
||||
if ((offset[i] inside {[0:127]}) && (offset[i] % 4 == 0) &&
|
||||
(RV32C inside {riscv_instr_pkg::supported_isa}) &&
|
||||
enable_compressed_load_store) begin
|
||||
allowed_instr = {C_LW, C_SW, allowed_instr};
|
||||
end
|
||||
if (XLEN >= 64) begin
|
||||
allowed_instr = {LWU, LD, SD, allowed_instr};
|
||||
if ((offset[i] inside {[0:255]}) && (offset[i] % 8 == 0) &&
|
||||
(RV64C inside {riscv_instr_pkg::supported_isa}) &&
|
||||
enable_compressed_load_store) begin
|
||||
allowed_instr = {C_LD, C_SD, allowed_instr};
|
||||
end
|
||||
end
|
||||
end
|
||||
randomize_instr(instr, .skip_rs1(1'b1), .skip_imm(1'b1), .disable_dist(1'b1));
|
||||
instr.rs1 = rs1_reg;
|
||||
instr.set_imm(offset[i]);
|
||||
instr.process_load_store = 0;
|
||||
instr_list.push_back(instr);
|
||||
load_store_instr.push_back(instr);
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// A single load/store instruction
|
||||
class riscv_single_load_store_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store == 1;
|
||||
num_mixed_instr < 5;
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_single_load_store_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
||||
|
||||
// Back to back load/store instructions
|
||||
class riscv_load_store_stress_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
int unsigned max_instr_cnt = 30;
|
||||
int unsigned min_instr_cnt = 10;
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store inside {[min_instr_cnt:max_instr_cnt]};
|
||||
num_mixed_instr == 0;
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_load_store_stress_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
||||
|
||||
// Random load/store sequence
|
||||
// A random mix of load/store instructions and other instructions
|
||||
class riscv_load_store_rand_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store inside {[10:30]};
|
||||
num_mixed_instr inside {[10:30]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_load_store_rand_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
||||
|
||||
// Use a small set of GPR to create various WAW, RAW, WAR hazard scenario
|
||||
class riscv_hazard_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
int unsigned num_of_avail_regs = 6;
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store inside {[10:30]};
|
||||
num_mixed_instr inside {[10:30]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_hazard_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
function void pre_randomize();
|
||||
avail_regs = new[num_of_avail_regs];
|
||||
super.pre_randomize();
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// Use a small set of address to create various load/store hazard sequence
|
||||
// This instruction stream focus more on hazard handling of load store unit.
|
||||
class riscv_load_store_hazard_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
rand int avail_addr[];
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store inside {[10:30]};
|
||||
num_mixed_instr inside {[10:30]};
|
||||
}
|
||||
|
||||
constraint avail_addr_c {
|
||||
avail_addr.size() inside {[1:3]};
|
||||
foreach(avail_addr[i]) {
|
||||
avail_addr[i] inside {[0 : max_load_store_offset - 1]};
|
||||
}
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_load_store_hazard_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
// Randomize each address in the post_randomize to reduce the complexity of solving everything
|
||||
// in one shot.
|
||||
function void post_randomize();
|
||||
int temp_addr;
|
||||
foreach(addr[i]) begin
|
||||
`DV_CHECK_STD_RANDOMIZE_WITH_FATAL(temp_addr,
|
||||
temp_addr inside {avail_addr};,
|
||||
"Cannot randomize address")
|
||||
addr[i] = temp_addr;
|
||||
end
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Back to back access to multiple data pages
|
||||
// This is useful to test data TLB switch and replacement
|
||||
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;
|
||||
rand int unsigned data_page_id[];
|
||||
rand riscv_reg_t rs1_reg[];
|
||||
|
||||
constraint default_c {
|
||||
foreach(data_page_id[i]) {
|
||||
data_page_id[i] < max_data_page_id;
|
||||
}
|
||||
data_page_id.size() == num_of_instr_stream;
|
||||
rs1_reg.size() == num_of_instr_stream;
|
||||
unique {rs1_reg};
|
||||
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 {
|
||||
num_of_instr_stream inside {[2:8]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_multi_page_load_store_instr_stream)
|
||||
`uvm_object_new
|
||||
|
||||
// Generate each load/store seq, and mix them together
|
||||
function void post_randomize();
|
||||
load_store_instr_stream = new[num_of_instr_stream];
|
||||
foreach(load_store_instr_stream[i]) begin
|
||||
load_store_instr_stream[i] = riscv_load_store_stress_instr_stream::type_id::
|
||||
create($sformatf("load_store_instr_stream_%0d", i));
|
||||
load_store_instr_stream[i].min_instr_cnt = 5;
|
||||
load_store_instr_stream[i].max_instr_cnt = 10;
|
||||
load_store_instr_stream[i].cfg = cfg;
|
||||
// Make sure each load/store sequence doesn't override the rs1 of other sequences.
|
||||
foreach(rs1_reg[j]) begin
|
||||
if(i != j) begin
|
||||
load_store_instr_stream[i].reserved_rd =
|
||||
{load_store_instr_stream[i].reserved_rd, rs1_reg[j]};
|
||||
end
|
||||
end
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(load_store_instr_stream[i],
|
||||
rs1_reg == local::rs1_reg[i];
|
||||
data_page_id == local::data_page_id[i];,
|
||||
"Cannot randomize load/store instruction")
|
||||
// Mix the instruction stream of different page access, this could trigger the scenario of
|
||||
// frequent data TLB switch
|
||||
if(i == 0) begin
|
||||
instr_list = load_store_instr_stream[i].instr_list;
|
||||
end else begin
|
||||
mix_instr_stream(load_store_instr_stream[i].instr_list);
|
||||
end
|
||||
end
|
||||
endfunction
|
||||
|
||||
endclass
|
||||
|
||||
// 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
|
||||
|
||||
// Random load/store sequence to full address range
|
||||
// The address range is not preloaded with data pages, use store instruction to initialize first
|
||||
class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_instr_stream;
|
||||
|
||||
rand bit [XLEN-1:0] addr_offset;
|
||||
|
||||
// Find an unused 4K page from address 1M onward
|
||||
constraint addr_offset_c {
|
||||
|addr_offset[XLEN-1:20] == 1'b1;
|
||||
// TODO(taliu) Support larger address range
|
||||
addr_offset[XLEN-1:31] == 0;
|
||||
addr_offset[11:0] == 0;
|
||||
}
|
||||
|
||||
constraint legal_c {
|
||||
num_load_store inside {[5:10]};
|
||||
num_mixed_instr inside {[5:10]};
|
||||
}
|
||||
|
||||
`uvm_object_utils(riscv_load_store_rand_addr_instr_stream)
|
||||
|
||||
virtual function void randomize_offset();
|
||||
int offset_, addr_;
|
||||
offset = new[num_load_store];
|
||||
addr = new[num_load_store];
|
||||
for (int i=0; i<num_load_store; i++) begin
|
||||
if (!std::randomize(offset_) with {
|
||||
offset_ inside {[-2048:2047]};
|
||||
}
|
||||
) begin
|
||||
`uvm_fatal(`gfn, "Cannot randomize load/store offset")
|
||||
end
|
||||
offset[i] = offset_;
|
||||
addr[i] = addr_offset + offset_;
|
||||
end
|
||||
endfunction `uvm_object_new
|
||||
|
||||
virtual function void add_rs1_init_la_instr(riscv_reg_t gpr, int id, int base = 0);
|
||||
riscv_instr_base instr[$];
|
||||
riscv_pseudo_instr li_instr;
|
||||
riscv_instr_base store_instr;
|
||||
riscv_instr_base add_instr;
|
||||
int min_offset[$];
|
||||
int max_offset[$];
|
||||
min_offset = offset.min();
|
||||
max_offset = offset.max();
|
||||
// Use LI to initialize the address offset
|
||||
li_instr = riscv_pseudo_instr::type_id::create("li_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(li_instr,
|
||||
pseudo_instr_name == LI;
|
||||
rd inside {cfg.gpr};
|
||||
rd != gpr;
|
||||
)
|
||||
li_instr.imm_str = $sformatf("0x%0x", addr_offset);
|
||||
// Add offset to the base address
|
||||
add_instr = riscv_instr_base::type_id::create("add_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(add_instr,
|
||||
instr_name == ADD;
|
||||
rs1 == gpr;
|
||||
rs2 == li_instr.rd;
|
||||
rd == gpr;
|
||||
)
|
||||
instr.push_back(li_instr);
|
||||
instr.push_back(add_instr);
|
||||
// Create SW instruction template
|
||||
store_instr = riscv_instr_base::type_id::create("store_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(store_instr,
|
||||
instr_name == SB;
|
||||
rs1 == gpr;
|
||||
)
|
||||
// Initialize the location which used by load instruction later
|
||||
foreach (load_store_instr[i]) begin
|
||||
if (load_store_instr[i].category == LOAD) begin
|
||||
riscv_instr_base store;
|
||||
store = riscv_instr_base::type_id::create("store");
|
||||
store.copy_base_instr(store_instr);
|
||||
store.rs2 = riscv_reg_t'(i % 32);
|
||||
store.imm_str = load_store_instr[i].imm_str;
|
||||
case (load_store_instr[i].instr_name) inside
|
||||
LB, LBU : store.instr_name = SB;
|
||||
LH, LHU : store.instr_name = SH;
|
||||
LW, C_LW, FLW, C_FLW : store.instr_name = SW;
|
||||
LD, C_LD, FLD, C_FLD, LWU : store.instr_name = SD;
|
||||
default : `uvm_fatal(`gfn, $sformatf("Unexpected op: %0s",
|
||||
load_store_instr[i].convert2asm()))
|
||||
endcase
|
||||
instr.push_back(store);
|
||||
end
|
||||
end
|
||||
instr_list = {instr, instr_list};
|
||||
super.add_rs1_init_la_instr(gpr, id, 0);
|
||||
endfunction
|
||||
|
||||
endclass
|
|
@ -1,212 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// TODO: Support other loop counter update instruction other than ADDI
|
||||
// TODO: Support forward branch inside the loop
|
||||
|
||||
class riscv_loop_instr extends riscv_rand_instr_stream;
|
||||
|
||||
rand riscv_reg_t loop_cnt_reg[];
|
||||
rand riscv_reg_t loop_limit_reg[];
|
||||
rand int loop_init_val[];
|
||||
rand int loop_step_val[];
|
||||
rand int loop_limit_val[];
|
||||
rand bit [2:0] num_of_nested_loop;
|
||||
rand 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[];
|
||||
riscv_rand_instr loop_branch_target_instr[];
|
||||
// Aggregated loop instruction stream
|
||||
riscv_instr_base loop_instr[];
|
||||
|
||||
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.reserved_regs[j]) {
|
||||
loop_cnt_reg[i] != cfg.reserved_regs[j];
|
||||
}
|
||||
}
|
||||
foreach (loop_limit_reg[i]) {
|
||||
foreach (cfg.reserved_regs[j]) {
|
||||
loop_limit_reg[i] != cfg.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;
|
||||
solve loop_limit_val before loop_limit_reg;
|
||||
solve branch_type before loop_init_val;
|
||||
solve branch_type before loop_step_val;
|
||||
solve branch_type before loop_limit_val;
|
||||
num_of_instr_in_loop inside {[1:25]};
|
||||
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 (branch_type[i]) {
|
||||
if (!cfg.disable_compressed_instr) {
|
||||
branch_type[i] inside {C_BNEZ, C_BEQZ, BEQ, BNE, BLTU, BLT, BGEU, BGE};
|
||||
} else {
|
||||
branch_type[i] inside {BEQ, BNE, BLTU, BLT, BGEU, BGE};
|
||||
}
|
||||
}
|
||||
foreach(loop_init_val[i]) {
|
||||
if (branch_type[i] inside {C_BNEZ, C_BEQZ}) {
|
||||
loop_limit_val[i] == 0;
|
||||
loop_limit_reg[i] == ZERO;
|
||||
loop_cnt_reg[i] inside {riscv_instr_pkg::compressed_gpr};
|
||||
} else {
|
||||
loop_limit_val[i] inside {[-20:20]};
|
||||
loop_limit_reg[i] != ZERO;
|
||||
}
|
||||
if (branch_type[i] inside {C_BNEZ, C_BEQZ, BEQ, BNE}) {
|
||||
((loop_limit_val[i] - loop_init_val[i]) % loop_step_val[i] == 0) &&
|
||||
(loop_limit_val[i] != loop_init_val[i]);
|
||||
} else if (branch_type[i] == BGE) {
|
||||
loop_step_val[i] < 0;
|
||||
} else if (branch_type[i] == BGEU) {
|
||||
loop_step_val[i] < 0;
|
||||
loop_init_val[i] > 0;
|
||||
// Avoid count to negative
|
||||
loop_step_val[i] + loop_limit_val[i] > 0;
|
||||
} else if (branch_type[i] == BLT) {
|
||||
loop_step_val[i] > 0;
|
||||
} else if (branch_type[i] == BLTU) {
|
||||
loop_step_val[i] > 0;
|
||||
loop_limit_val[i] > 0;
|
||||
}
|
||||
loop_init_val[i] inside {[-10:10]};
|
||||
loop_step_val[i] inside {[-10:10]};
|
||||
if(loop_init_val[i] < loop_limit_val[i]) {
|
||||
loop_step_val[i] > 0;
|
||||
} else {
|
||||
loop_step_val[i] < 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
`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);
|
||||
// Randomize the key loop instructions
|
||||
loop_init_instr = new[num_of_nested_loop*2];
|
||||
loop_update_instr = new[num_of_nested_loop];
|
||||
loop_branch_instr = new[num_of_nested_loop];
|
||||
loop_branch_target_instr = new[num_of_nested_loop];
|
||||
for(int i = 0; i < num_of_nested_loop; i++) begin
|
||||
// Instruction to init the loop counter
|
||||
loop_init_instr[2*i] = riscv_instr_base::type_id::create("loop_init_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i],
|
||||
instr_name == ADDI;
|
||||
rd == loop_cnt_reg[i];
|
||||
rs1 == ZERO;
|
||||
imm == loop_init_val[i];,
|
||||
"Cannot randomize loop init insturction")
|
||||
loop_init_instr[2*i].comment = $sformatf("init loop %0d counter", i);
|
||||
|
||||
// Instruction to init loop limit
|
||||
loop_init_instr[2*i+1] = riscv_instr_base::type_id::create("loop_init_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_init_instr[2*i+1],
|
||||
instr_name == ADDI;
|
||||
rd == loop_limit_reg[i];
|
||||
rs1 == ZERO;
|
||||
imm == loop_limit_val[i];,
|
||||
"Cannot randomize init loop instruction")
|
||||
loop_init_instr[2*i+1].comment = $sformatf("init loop %0d limit", i);
|
||||
|
||||
// Branch target instruction, can be anything
|
||||
loop_branch_target_instr[i] = riscv_rand_instr::type_id::create("loop_branch_target_instr");
|
||||
loop_branch_target_instr[i].cfg = cfg;
|
||||
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});
|
||||
// TODO: allow CSR instructions in all modes
|
||||
if (cfg.init_privileged_mode == MACHINE_MODE) {
|
||||
if (category == CSR) {
|
||||
csr == MSCRATCH;
|
||||
}
|
||||
} else {
|
||||
!(category == CSR);
|
||||
},
|
||||
"Cannot randomize branch target instruction")
|
||||
loop_branch_target_instr[i].label = $sformatf("%0s_%0d_t", label, i);
|
||||
|
||||
// Instruction to update loop counter
|
||||
loop_update_instr[i] = riscv_instr_base::type_id::create("loop_update_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_update_instr[i],
|
||||
instr_name == ADDI;
|
||||
rd == loop_cnt_reg[i];
|
||||
rs1== loop_cnt_reg[i];
|
||||
imm == loop_step_val[i];,
|
||||
"Cannot randomize loop update instruction")
|
||||
loop_update_instr[i].comment = $sformatf("update loop %0d counter", i);
|
||||
|
||||
// Backward branch instruction
|
||||
loop_branch_instr[i] = riscv_instr_base::type_id::create("loop_branch_instr");
|
||||
`DV_CHECK_RANDOMIZE_WITH_FATAL(loop_branch_instr[i],
|
||||
instr_name == branch_type[i];
|
||||
rs1 == loop_cnt_reg[i];
|
||||
if (!(branch_type[i] inside {C_BEQZ, C_BNEZ})) {
|
||||
rs2 == loop_limit_reg[i];
|
||||
},
|
||||
"Cannot randomize backward branch instruction")
|
||||
loop_branch_instr[i].comment = $sformatf("branch for loop %0d", i);
|
||||
loop_branch_instr[i].imm_str = loop_branch_target_instr[i].label;
|
||||
loop_branch_instr[i].branch_assigned = 1'b1;
|
||||
end
|
||||
// Randomly distribute the loop instruction in the existing instruction stream
|
||||
build_loop_instr_stream();
|
||||
mix_instr_stream(loop_instr, 1'b1);
|
||||
foreach(instr_list[i]) begin
|
||||
if(instr_list[i].label != "")
|
||||
instr_list[i].has_label = 1'b1;
|
||||
else
|
||||
instr_list[i].has_label = 1'b0;
|
||||
instr_list[i].atomic = 1'b1;
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Build the whole loop structure from innermost loop to the outermost loop
|
||||
function void build_loop_instr_stream();
|
||||
loop_instr.delete;
|
||||
for(int i = 0; i < num_of_nested_loop; i++) begin
|
||||
loop_instr = {loop_init_instr[2*i],
|
||||
loop_init_instr[2*i+1],
|
||||
loop_branch_target_instr[i],
|
||||
loop_update_instr[i],
|
||||
loop_instr,
|
||||
loop_branch_instr[i]};
|
||||
end
|
||||
`uvm_info(get_full_name(), $sformatf("Totally %0d instructions have been added",
|
||||
loop_instr.size()), UVM_HIGH)
|
||||
endfunction
|
||||
endclass
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class riscv_rand_instr extends riscv_instr_base;
|
||||
|
||||
riscv_instr_gen_config cfg;
|
||||
|
||||
// Some additional reserved registers
|
||||
riscv_reg_t reserved_rd[];
|
||||
|
||||
`uvm_object_utils(riscv_rand_instr)
|
||||
|
||||
constraint category_c {
|
||||
soft category inside {LOAD, STORE, SHIFT, ARITHMETIC, LOGICAL,
|
||||
BRANCH, COMPARE, CSR, SYSTEM, SYNCH};
|
||||
}
|
||||
|
||||
// Atomic extension instructions are default disabled
|
||||
// AMO instruction generation is handled by riscv_amo_instr_lib.sv
|
||||
constraint disable_a_extension_c {
|
||||
group != RV32A;
|
||||
group != RV64A;
|
||||
}
|
||||
|
||||
constraint instr_c {
|
||||
solve instr_name before imm;
|
||||
solve instr_name before rs1;
|
||||
solve instr_name before rs2;
|
||||
!(instr_name inside {riscv_instr_pkg::unsupported_instr});
|
||||
group inside {riscv_instr_pkg::supported_isa};
|
||||
// Avoid using any special purpose register as rd, those registers are reserved for
|
||||
// special instructions
|
||||
!(rd inside {cfg.reserved_regs});
|
||||
if(reserved_rd.size() > 0) {
|
||||
!(rd inside {reserved_rd});
|
||||
}
|
||||
// Compressed instruction may use the same CSR for both rs1 and rd
|
||||
if(group inside {RV32C, RV64C, RV128C, RV32FC, RV32DC}) {
|
||||
!(rs1 inside {cfg.reserved_regs, reserved_rd});
|
||||
}
|
||||
// Below instrutions will modify stack pointer, not allowed in normal instruction stream.
|
||||
// It can be used in stack operation instruction stream.
|
||||
!(instr_name inside {C_SWSP, C_SDSP, C_ADDI16SP});
|
||||
// Avoid using reserved registers as rs1 (base address)
|
||||
if(category inside {LOAD, STORE}) {
|
||||
!(rs1 inside {reserved_rd, cfg.reserved_regs, ZERO});
|
||||
}
|
||||
if(!cfg.enable_sfence) {
|
||||
instr_name != SFENCE_VMA;
|
||||
}
|
||||
if(cfg.no_fence) {
|
||||
!(instr_name inside {FENCE, FENCE_I, SFENCE_VMA});
|
||||
}
|
||||
// TODO: Support C_ADDI4SPN
|
||||
instr_name != C_ADDI4SPN;
|
||||
}
|
||||
|
||||
constraint constraint_cfg_knob_c {
|
||||
if(cfg.no_ebreak) {
|
||||
instr_name != EBREAK;
|
||||
instr_name != C_EBREAK;
|
||||
}
|
||||
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) {
|
||||
category != LOAD;
|
||||
category != STORE;
|
||||
}
|
||||
if(cfg.no_branch_jump) {
|
||||
category != BRANCH;
|
||||
}
|
||||
if (cfg.disable_compressed_instr) {
|
||||
!(group inside {RV32C, RV64C, RV128C, RV32FC, RV32DC});
|
||||
}
|
||||
}
|
||||
|
||||
constraint csr_instr_c {
|
||||
// TODO: support CSR instruction in other modes
|
||||
if(cfg.no_csr_instr) {
|
||||
category != CSR;
|
||||
} else {
|
||||
if (category == CSR) {
|
||||
if (cfg.enable_illegal_csr_instruction) {
|
||||
!(csr inside {implemented_csr});
|
||||
} else if (cfg.enable_access_invalid_csr_level) {
|
||||
csr inside {cfg.invalid_priv_mode_csrs};
|
||||
} else {
|
||||
// Use scratch register to avoid the side effect of modifying other privileged mode CSR.
|
||||
if (cfg.init_privileged_mode == MACHINE_MODE) {
|
||||
if (MSCRATCH inside {implemented_csr}) {
|
||||
csr == MSCRATCH;
|
||||
}
|
||||
} else if (cfg.init_privileged_mode == SUPERVISOR_MODE) {
|
||||
if (SSCRATCH inside {implemented_csr}) {
|
||||
csr == SSCRATCH;
|
||||
}
|
||||
} else {
|
||||
if (USCRATCH inside {implemented_csr}) {
|
||||
csr == USCRATCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
constraint floating_point_c {
|
||||
if (!cfg.enable_floating_point) {
|
||||
!(group inside {RV32F, RV64F, RV32D, RV64D});
|
||||
}
|
||||
}
|
||||
|
||||
function void pre_randomize();
|
||||
if (!cfg.enable_floating_point) begin
|
||||
fs1.rand_mode(0);
|
||||
fs2.rand_mode(0);
|
||||
fs3.rand_mode(0);
|
||||
fd.rand_mode(0);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// No label is needed if there's no branch/jump instruction
|
||||
function void post_randomize();
|
||||
super.post_randomize();
|
||||
if (cfg.no_branch_jump) begin
|
||||
has_label = 1'b0;
|
||||
end
|
||||
endfunction
|
||||
|
||||
`uvm_object_new
|
||||
|
||||
endclass
|
|
@ -1,4 +1,4 @@
|
|||
class riscv_instr_cov_item extends `INSTR;
|
||||
class riscv_instr_cov_item extends riscv_instr;
|
||||
|
||||
typedef enum bit[1:0] {
|
||||
POSITIVE, NEGATIVE
|
||||
|
|
|
@ -1315,17 +1315,9 @@ class riscv_instr_cover_group;
|
|||
riscv_instr_name_t instr_name;
|
||||
instr_name = instr_name.first;
|
||||
do begin
|
||||
`INSTR instr;
|
||||
riscv_instr instr;
|
||||
if (!(instr_name inside {unsupported_instr}) && (instr_name != INVALID_INSTR)) begin
|
||||
`ifdef DEPRECATED
|
||||
instr = riscv_instr_base::type_id::create("instr");
|
||||
if (!instr.randomize() with {instr_name == local::instr_name;}) begin
|
||||
`uvm_fatal("riscv_instr_cover_group",
|
||||
$sformatf("Instruction %0s randomization failure", instr_name.name()))
|
||||
end
|
||||
`else
|
||||
instr = riscv_instr::create_instr(instr_name);
|
||||
`endif
|
||||
instr = riscv_instr::create_instr(instr_name);
|
||||
if ((instr.group inside {supported_isa}) &&
|
||||
(instr.group inside {RV32I, RV32M, RV64M, RV64I, RV32C, RV64C,
|
||||
RV32V, RV64V, RV64B, RV32B})) begin
|
||||
|
|
70
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
70
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
|
@ -1080,40 +1080,33 @@ package riscv_instr_pkg;
|
|||
SYNCH, SYSTEM, COUNTER, CSR, CHANGELEVEL, TRAP, INTERRUPT, AMO
|
||||
};
|
||||
|
||||
`include "riscv_vector_cfg.sv"
|
||||
`ifdef DEPRECATED
|
||||
`define INSTR riscv_instr_base
|
||||
`include "deprecated/riscv_instr_base.sv"
|
||||
`include "deprecated/riscv_instr_gen_config.sv"
|
||||
`else
|
||||
`define INSTR riscv_instr
|
||||
typedef class riscv_instr;
|
||||
`include "riscv_instr_gen_config.sv"
|
||||
`include "isa/riscv_instr.sv"
|
||||
`include "isa/riscv_amo_instr.sv"
|
||||
`include "isa/riscv_floating_point_instr.sv"
|
||||
`include "isa/riscv_vector_instr.sv"
|
||||
`include "isa/riscv_compressed_instr.sv"
|
||||
`include "isa/rv32a_instr.sv"
|
||||
`include "isa/rv32c_instr.sv"
|
||||
`include "isa/rv32dc_instr.sv"
|
||||
`include "isa/rv32d_instr.sv"
|
||||
`include "isa/rv32fc_instr.sv"
|
||||
`include "isa/rv32f_instr.sv"
|
||||
`include "isa/rv32i_instr.sv"
|
||||
`include "isa/rv32m_instr.sv"
|
||||
`include "isa/rv64a_instr.sv"
|
||||
`include "isa/rv64c_instr.sv"
|
||||
`include "isa/rv64d_instr.sv"
|
||||
`include "isa/rv64f_instr.sv"
|
||||
`include "isa/rv64i_instr.sv"
|
||||
`include "isa/rv64m_instr.sv"
|
||||
`include "isa/rv128c_instr.sv"
|
||||
`include "isa/rv32v_instr.sv"
|
||||
`include "isa/custom/riscv_custom_instr.sv"
|
||||
`include "isa/custom/rv32x_instr.sv"
|
||||
`include "isa/custom/rv64x_instr.sv"
|
||||
`endif
|
||||
`include "riscv_vector_cfg.sv"
|
||||
typedef class riscv_instr;
|
||||
`include "riscv_instr_gen_config.sv"
|
||||
`include "isa/riscv_instr.sv"
|
||||
`include "isa/riscv_amo_instr.sv"
|
||||
`include "isa/riscv_floating_point_instr.sv"
|
||||
`include "isa/riscv_vector_instr.sv"
|
||||
`include "isa/riscv_compressed_instr.sv"
|
||||
`include "isa/rv32a_instr.sv"
|
||||
`include "isa/rv32c_instr.sv"
|
||||
`include "isa/rv32dc_instr.sv"
|
||||
`include "isa/rv32d_instr.sv"
|
||||
`include "isa/rv32fc_instr.sv"
|
||||
`include "isa/rv32f_instr.sv"
|
||||
`include "isa/rv32i_instr.sv"
|
||||
`include "isa/rv32m_instr.sv"
|
||||
`include "isa/rv64a_instr.sv"
|
||||
`include "isa/rv64c_instr.sv"
|
||||
`include "isa/rv64d_instr.sv"
|
||||
`include "isa/rv64f_instr.sv"
|
||||
`include "isa/rv64i_instr.sv"
|
||||
`include "isa/rv64m_instr.sv"
|
||||
`include "isa/rv128c_instr.sv"
|
||||
`include "isa/rv32v_instr.sv"
|
||||
`include "isa/custom/riscv_custom_instr.sv"
|
||||
`include "isa/custom/rv32x_instr.sv"
|
||||
`include "isa/custom/rv64x_instr.sv"
|
||||
|
||||
`include "riscv_pseudo_instr.sv"
|
||||
`include "riscv_illegal_instr.sv"
|
||||
|
@ -1127,20 +1120,11 @@ package riscv_instr_pkg;
|
|||
`include "riscv_callstack_gen.sv"
|
||||
`include "riscv_data_page_gen.sv"
|
||||
|
||||
`ifdef DEPRECATED
|
||||
`include "deprecated/riscv_rand_instr.sv"
|
||||
`include "deprecated/riscv_instr_stream.sv"
|
||||
`include "deprecated/riscv_loop_instr.sv"
|
||||
`include "deprecated/riscv_directed_instr_lib.sv"
|
||||
`include "deprecated/riscv_load_store_instr_lib.sv"
|
||||
`include "deprecated/riscv_amo_instr_lib.sv"
|
||||
`else
|
||||
`include "riscv_instr_stream.sv"
|
||||
`include "riscv_loop_instr.sv"
|
||||
`include "riscv_directed_instr_lib.sv"
|
||||
`include "riscv_load_store_instr_lib.sv"
|
||||
`include "riscv_amo_instr_lib.sv"
|
||||
`endif
|
||||
|
||||
`include "riscv_instr_sequence.sv"
|
||||
`include "riscv_asm_program_gen.sv"
|
||||
|
|
14
vendor/google_riscv-dv/src/riscv_instr_stream.sv
vendored
14
vendor/google_riscv-dv/src/riscv_instr_stream.sv
vendored
|
@ -20,7 +20,7 @@
|
|||
// instruction, mix two instruction streams etc.
|
||||
class riscv_instr_stream extends uvm_object;
|
||||
|
||||
`INSTR instr_list[$];
|
||||
riscv_instr instr_list[$];
|
||||
int unsigned instr_cnt;
|
||||
string label = "";
|
||||
// User can specify a small group of available registers to generate various hazard condition
|
||||
|
@ -40,16 +40,16 @@ class riscv_instr_stream extends uvm_object;
|
|||
endfunction
|
||||
|
||||
virtual function void create_instr_instance();
|
||||
`INSTR instr;
|
||||
riscv_instr instr;
|
||||
for(int i = 0; i < instr_cnt; i++) begin
|
||||
instr = `INSTR::type_id::create($sformatf("instr_%0d", i));
|
||||
instr = riscv_instr::type_id::create($sformatf("instr_%0d", i));
|
||||
instr_list.push_back(instr);
|
||||
end
|
||||
endfunction
|
||||
|
||||
// Insert an instruction to the existing instruction stream at the given index
|
||||
// When index is -1, the instruction is injected at a random location
|
||||
function void insert_instr(`INSTR instr, int idx = -1);
|
||||
function void insert_instr(riscv_instr instr, int idx = -1);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
if(idx == -1) begin
|
||||
idx = $urandom_range(0, current_instr_cnt-1);
|
||||
|
@ -70,7 +70,7 @@ class riscv_instr_stream extends uvm_object;
|
|||
// Insert an instruction to the existing instruction stream at the given index
|
||||
// When index is -1, the instruction is injected at a random location
|
||||
// When replace is 1, the original instruction at the inserted position will be replaced
|
||||
function void insert_instr_stream(`INSTR new_instr[], int idx = -1, bit replace = 1'b0);
|
||||
function void insert_instr_stream(riscv_instr new_instr[], int idx = -1, bit replace = 1'b0);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
int new_instr_cnt = new_instr.size();
|
||||
if(current_instr_cnt == 0) begin
|
||||
|
@ -119,7 +119,7 @@ class riscv_instr_stream extends uvm_object;
|
|||
// Mix the input instruction stream with the original instruction, the instruction order is
|
||||
// preserved. When 'contained' is set, the original instruction stream will be inside the
|
||||
// new instruction stream with the first and last instruction from the input instruction stream.
|
||||
function void mix_instr_stream(`INSTR new_instr[], bit contained = 1'b0);
|
||||
function void mix_instr_stream(riscv_instr new_instr[], bit contained = 1'b0);
|
||||
int current_instr_cnt = instr_list.size();
|
||||
int insert_instr_position[];
|
||||
int new_instr_cnt = new_instr.size();
|
||||
|
@ -167,7 +167,7 @@ class riscv_rand_instr_stream extends riscv_instr_stream;
|
|||
`uvm_object_new
|
||||
|
||||
virtual function void create_instr_instance();
|
||||
`INSTR instr;
|
||||
riscv_instr instr;
|
||||
for (int i = 0; i < instr_cnt; i++) begin
|
||||
instr_list.push_back(null);
|
||||
end
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Psuedo instructions are used to simplify assembly program writing
|
||||
class riscv_pseudo_instr extends `INSTR;
|
||||
class riscv_pseudo_instr extends riscv_instr;
|
||||
|
||||
rand riscv_pseudo_instr_name_t pseudo_instr_name;
|
||||
|
||||
|
|
|
@ -80,17 +80,10 @@ class riscv_instr_base_test extends uvm_test;
|
|||
|
||||
task run_phase(uvm_phase phase);
|
||||
int fd;
|
||||
`ifdef DEPRECATED
|
||||
cfg.build_instruction_template();
|
||||
`endif
|
||||
for(int i = 0; i < cfg.num_of_tests; i++) begin
|
||||
string test_name;
|
||||
randomize_cfg();
|
||||
`ifdef DEPRECATED
|
||||
cfg.build_instruction_list();
|
||||
`else
|
||||
riscv_instr::create_instr_list(cfg);
|
||||
`endif
|
||||
riscv_instr::create_instr_list(cfg);
|
||||
asm_gen = riscv_asm_program_gen::type_id::create("asm_gen", , `gfn);
|
||||
asm_gen.cfg = cfg;
|
||||
asm_gen.get_directed_instr_stream();
|
||||
|
|
|
@ -39,17 +39,9 @@ class riscv_instr_cov_test extends uvm_test;
|
|||
cfg = riscv_instr_gen_config::type_id::create("cfg");
|
||||
// disable_compressed_instr is not relevant to coverage test
|
||||
cfg.disable_compressed_instr = 0;
|
||||
`ifdef DEPRECATED
|
||||
cfg.build_instruction_template(.skip_instr_exclusion(1));
|
||||
`else
|
||||
riscv_instr::create_instr_list(cfg);
|
||||
`endif
|
||||
riscv_instr::create_instr_list(cfg);
|
||||
instr = riscv_instr_cov_item::type_id::create("instr");
|
||||
instr.rand_mode(0);
|
||||
`ifdef DEPRECATED
|
||||
instr.no_hint_illegal_instr_c.constraint_mode(0);
|
||||
instr.imm_val_c.constraint_mode(0);
|
||||
`endif
|
||||
instr_cg = new(cfg);
|
||||
`uvm_info(`gfn, $sformatf("%0d CSV trace files to be processed", trace_csv.size()), UVM_LOW)
|
||||
foreach (trace_csv[i]) begin
|
||||
|
@ -142,13 +134,8 @@ class riscv_instr_cov_test extends uvm_test;
|
|||
`SAMPLE(instr_cg.opcode_cg, binary[6:2])
|
||||
end
|
||||
if (instr_enum::from_name(process_instr_name(trace["instr"]), instr_name)) begin
|
||||
`ifdef DEPRECATED
|
||||
if (cfg.instr_template.exists(instr_name)) begin
|
||||
instr.copy_base_instr(cfg.instr_template[instr_name]);
|
||||
`else
|
||||
if (riscv_instr::instr_template.exists(instr_name)) begin
|
||||
instr.copy(riscv_instr::instr_template[instr_name]);
|
||||
`endif
|
||||
if (instr.group inside {RV32I, RV32M, RV32C, RV64I, RV64M, RV64C}) begin
|
||||
assign_trace_info_to_instr(instr);
|
||||
end
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
# no_iss : Enable/disable ISS simulator (Optional)
|
||||
# gen_test : Test name used by the instruction generator
|
||||
# asm_tests : Path to directed, hand-coded assembly test file or directory
|
||||
# c_tests : Path to directed, hand-coded C test file or directory
|
||||
# rtl_test : RTL simulation test name
|
||||
# cmp_opts : Compile options passed to the instruction generator
|
||||
# sim_opts : Simulation options passed to the instruction generator
|
||||
|
|
2
vendor/google_riscv-dv/yaml/simulator.yaml
vendored
2
vendor/google_riscv-dv/yaml/simulator.yaml
vendored
|
@ -68,7 +68,6 @@
|
|||
cov_opts: >
|
||||
-do "coverage save -onexit <out>/cov.ucdb;"
|
||||
|
||||
# TODO: Remove +DEPRECATED for dsim
|
||||
- tool: dsim
|
||||
env_var: DSIM,DSIM_LIB_PATH
|
||||
compile:
|
||||
|
@ -77,7 +76,6 @@
|
|||
- "<DSIM> -sv -work <out>/dsim
|
||||
-genimage image
|
||||
+incdir+$UVM_HOME/src
|
||||
+define+DEPRECATED
|
||||
$UVM_HOME/src/uvm_pkg.sv
|
||||
+define+DSIM
|
||||
+incdir+<setting>
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue