blob: 30a5343e59673653ad6da3067b0a4eabb5809299 [file] [log] [blame]
/*
* 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