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:
udinator 2020-01-28 15:45:41 -08:00 committed by GitHub
parent 2e258c8521
commit 230c282c36
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 104 additions and 4108 deletions

View file

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

View file

@ -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.

View file

@ -1,6 +1,7 @@
-64
-uvmhome uvm-1.2
-sv
-access=rwc+/.
-mfcu
-cuname design_cuname
+define+UVM_REGEX_NO_DPI

View file

@ -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)

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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"

View file

@ -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

View file

@ -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;

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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>