Skip to content

Commit

Permalink
CVA6-DV : Add unsupported extension instruction for the embedded config
Browse files Browse the repository at this point in the history
  • Loading branch information
AyoubJalali committed Sep 25, 2023
1 parent c71690a commit 9974ac1
Show file tree
Hide file tree
Showing 7 changed files with 636 additions and 21 deletions.
151 changes: 130 additions & 21 deletions verif/env/corev-dv/cva6_asm_program_gen.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,121 @@

class cva6_asm_program_gen_c extends riscv_asm_program_gen;

cva6_instr_sequence_c cva6_main_program[NUM_HARTS];
cva6_instr_gen_config_c cfg_cva6; // Configuration class handle

`uvm_object_utils(cva6_asm_program_gen_c)

function new (string name = "");
super.new(name);
endfunction

// This is the main function to generate all sections of the program.
virtual function void gen_program();
instr_stream.delete();
// Generate program header
`DV_CHECK_FATAL($cast(cfg_cva6, cfg), "Could not cast cfg into cfg_cva6")
gen_program_header();
for (int hart = 0; hart < cfg_cva6.num_of_harts; hart++) begin
string sub_program_name[$];
instr_stream.push_back($sformatf("h%0d_start:", hart));
if (!cfg_cva6.bare_program_mode) begin
setup_misa();
// Create all page tables
create_page_table(hart);
// Setup privileged mode registers and enter target privileged mode
pre_enter_privileged_mode(hart);
end
// Init section
gen_init_section(hart);
// If PMP is supported, we want to generate the associated trap handlers and the test_done
// section at the start of the program so we can allow access through the pmpcfg0 CSR
if (support_pmp) begin
gen_trap_handlers(hart);
// Ecall handler
gen_ecall_handler(hart);
// Instruction fault handler
gen_instr_fault_handler(hart);
// Load fault handler
gen_load_fault_handler(hart);
// Store fault handler
gen_store_fault_handler(hart);
gen_test_done();
end
// Generate sub program
gen_sub_program(hart, sub_program[hart], sub_program_name, cfg_cva6.num_of_sub_program);
// Generate main program
cva6_main_program[hart] = cva6_instr_sequence_c::type_id::create(get_label("cva6_main", hart));
cva6_main_program[hart].instr_cnt = cfg_cva6.main_program_instr_cnt;
cva6_main_program[hart].is_debug_program = 0;
cva6_main_program[hart].label_name = cva6_main_program[hart].get_name();
generate_directed_instr_stream(.hart(hart),
.label(cva6_main_program[hart].label_name),
.original_instr_cnt(cva6_main_program[hart].instr_cnt),
.min_insert_cnt(1),
.instr_stream(cva6_main_program[hart].directed_instr));
cva6_main_program[hart].cfg_cva6 = cfg_cva6;
`DV_CHECK_RANDOMIZE_FATAL(cva6_main_program[hart])
cva6_main_program[hart].gen_instr(.is_main_program(1), .no_branch(cfg_cva6.no_branch_jump));
// Setup jump instruction among main program and sub programs
gen_callstack(cva6_main_program[hart], sub_program[hart], sub_program_name,
cfg_cva6.num_of_sub_program);
`uvm_info(`gfn, "Generating callstack...done", UVM_LOW)
cva6_main_program[hart].post_process_instr();
`uvm_info(`gfn, "Post-processing main program...done", UVM_LOW)
cva6_main_program[hart].generate_unsupported_instr_stream();
`uvm_info(`gfn, "Generating main program instruction stream...done", UVM_LOW)
instr_stream = {instr_stream, cva6_main_program[hart].instr_string_list};
// If PMP is supported, need to jump from end of main program to test_done section at the end
// of main_program, as the test_done will have moved to the beginning of the program
instr_stream = {instr_stream, $sformatf("%sj test_done", indent)};
// Test done section
// If PMP isn't supported, generate this in the normal location
if (hart == 0 & !support_pmp) begin
gen_test_done();
end
// Shuffle the sub programs and insert to the instruction stream
insert_sub_program(sub_program[hart], instr_stream);
`uvm_info(`gfn, "Inserting sub-programs...done", UVM_LOW)
`uvm_info(`gfn, "Main/sub program generation...done", UVM_LOW)
// Program end
gen_program_end(hart);
if (!cfg_cva6.bare_program_mode) begin
if (!riscv_instr_pkg::support_pmp) begin
// Privileged mode switch routine
gen_privileged_mode_switch_routine(hart);
end
// Generate debug rom section
if (riscv_instr_pkg::support_debug_mode) begin
gen_debug_rom(hart);
end
end
gen_section({hart_prefix(hart), "instr_end"}, {"nop"});
end
for (int hart = 0; hart < cfg_cva6.num_of_harts; hart++) begin
// Starting point of data section
gen_data_page_begin(hart);
if(!cfg_cva6.no_data_page) begin
// User data section
gen_data_page(hart);
// AMO memory region
if ((hart == 0) && (RV32A inside {supported_isa})) begin
gen_data_page(hart, .amo(1));
end
end
// Stack section
gen_stack_section(hart);
if (!cfg_cva6.bare_program_mode) begin
// Generate kernel program/data/stack section
gen_kernel_sections(hart);
end
// Page table
if (!cfg_cva6.bare_program_mode) begin
gen_page_table_section(hart);
end
end
endfunction

virtual function void gen_program_header();
string str[$];
cva6_instr_gen_config_c cfg_cva6;
Expand All @@ -46,16 +155,16 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
instr_stream.push_back(".include \"user_define.h\"");
instr_stream.push_back(".globl _start");
instr_stream.push_back(".section .text");
if (cfg.disable_compressed_instr) begin
if (cfg_cva6.disable_compressed_instr) begin
instr_stream.push_back(".option norvc;");
end
str = {"csrr x5, mhartid"};
for (int hart = 0; hart < cfg.num_of_harts; hart++) begin
for (int hart = 0; hart < cfg_cva6.num_of_harts; hart++) begin
str = {str, $sformatf("li x6, %0d", hart),
$sformatf("beq x5, x6, %0df", hart)};
end
gen_section("_start", str);
for (int hart = 0; hart < cfg.num_of_harts; hart++) begin
for (int hart = 0; hart < cfg_cva6.num_of_harts; hart++) begin
instr_stream.push_back($sformatf("%0d: j h%0d_start", hart, hart));
end
endfunction
Expand All @@ -72,58 +181,58 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
bit is_interrupt = 'b1;
string tvec_name;
string instr[$];
if (cfg.mtvec_mode == VECTORED) begin
if (cfg_cva6.mtvec_mode == VECTORED) begin
gen_interrupt_vector_table(hart, mode, status, cause, ie, ip, scratch, instr);
end else begin
// Push user mode GPR to kernel stack before executing exception handling, this is to avoid
// exception handling routine modify user program state unexpectedly
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
push_gpr_to_kernel_stack(status, scratch, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
// Checking xStatus can be optional if ISS (like spike) has different implementation of
// certain fields compared with the RTL processor.
if (cfg.check_xstatus) begin
instr = {instr, $sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], status, status.name())};
if (cfg_cva6.check_xstatus) begin
instr = {instr, $sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], status, status.name())};
end
instr = {instr,
// Use scratch CSR to save a GPR value
// Check if the exception is caused by an interrupt, if yes, jump to interrupt
// handler Interrupt is indicated by xCause[XLEN-1]
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], cause, cause.name()),
$sformatf("srli x%0d, x%0d, %0d", cfg.gpr[0], cfg.gpr[0], XLEN-1),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()),
$sformatf("srli x%0d, x%0d, %0d", cfg_cva6.gpr[0], cfg_cva6.gpr[0], XLEN-1),
$sformatf("bne x%0d, x0, %0s%0smode_intr_handler",
cfg.gpr[0], hart_prefix(hart), mode)};
cfg_cva6.gpr[0], hart_prefix(hart), mode)};
end
// The trap handler will occupy one 4KB page, it will be allocated one entry in the page table
// with a specific privileged mode.
if (SATP_MODE != BARE) begin
instr_stream.push_back(".align 12");
end else begin
instr_stream.push_back($sformatf(".align %d", cfg.tvec_alignment));
instr_stream.push_back($sformatf(".align %d", cfg_cva6.tvec_alignment));
end
tvec_name = tvec.name();
gen_section(get_label($sformatf("%0s_handler", tvec_name.tolower()), hart), instr);
// Exception handler
instr = {};
if (cfg.mtvec_mode == VECTORED) begin
push_gpr_to_kernel_stack(status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
if (cfg_cva6.mtvec_mode == VECTORED) begin
push_gpr_to_kernel_stack(status, scratch, cfg_cva6.mstatus_mprv, cfg_cva6.sp, cfg_cva6.tp, instr);
end
gen_signature_handshake(instr, CORE_STATUS, HANDLING_EXCEPTION);
instr = {instr,
// The trap is caused by an exception, read back xCAUSE, xEPC to see if these
// CSR values are set properly. The checking is done by comparing against the log
// generated by ISA simulator (spike).
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], epc, epc.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg.gpr[0], cause, cause.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], epc, epc.name()),
$sformatf("csrr x%0d, 0x%0x # %0s", cfg_cva6.gpr[0], cause, cause.name()),
// Breakpoint
$sformatf("li x%0d, 0x%0x # BREAKPOINT", cfg.gpr[1], BREAKPOINT),
$sformatf("li x%0d, 0x%0x # BREAKPOINT", cfg_cva6.gpr[1], BREAKPOINT),
$sformatf("beq x%0d, x%0d, %0sebreak_handler",
cfg.gpr[0], cfg.gpr[1], hart_prefix(hart)),
cfg_cva6.gpr[0], cfg_cva6.gpr[1], hart_prefix(hart)),
// Check if it's an ECALL exception. Jump to ECALL exception handler
$sformatf("li x%0d, 0x%0x # ECALL_UMODE", cfg.gpr[1], ECALL_UMODE),
$sformatf("li x%0d, 0x%0x # ECALL_UMODE", cfg_cva6.gpr[1], ECALL_UMODE),
$sformatf("beq x%0d, x%0d, %0secall_handler",
cfg.gpr[0], cfg.gpr[1], hart_prefix(hart)),
$sformatf("li x%0d, 0x%0x # ECALL_SMODE", cfg.gpr[1], ECALL_SMODE),
cfg_cva6.gpr[0], cfg_cva6.gpr[1], hart_prefix(hart)),
$sformatf("li x%0d, 0x%0x # ECALL_SMODE", cfg_cva6.gpr[1], ECALL_SMODE),
$sformatf("beq x%0d, x%0d, %0secall_handler",
cfg.gpr[0], cfg.gpr[1], hart_prefix(hart)),
cfg_cva6.gpr[0], cfg_cva6.gpr[1], hart_prefix(hart)),
$sformatf("li x%0d, 0x%0x # ECALL_MMODE", cfg.gpr[1], ECALL_MMODE),
$sformatf("beq x%0d, x%0d, %0secall_handler",
cfg.gpr[0], cfg.gpr[1], hart_prefix(hart)),
Expand Down
8 changes: 8 additions & 0 deletions verif/env/corev-dv/cva6_instr_base_test.sv
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ class cva6_instr_base_test_c extends riscv_instr_base_test;
virtual function void build_phase(uvm_phase phase);
override_asm_program_gen();
override_gen_config();
override_sequence_instr();
super.build_phase(phase);
endfunction

Expand All @@ -51,4 +52,11 @@ class cva6_instr_base_test_c extends riscv_instr_base_test;
`uvm_info("CVA6_DV", $sformatf("Overrid done "), UVM_LOW)
endfunction

virtual function void override_sequence_instr();
`uvm_info("CVA6_DV", $sformatf("Overriding ..."), UVM_LOW)
uvm_factory::get().set_type_override_by_type(riscv_instr_sequence::get_type(),
cva6_instr_sequence_c::get_type());
`uvm_info("CVA6_DV", $sformatf("Overrid done "), UVM_LOW)
endfunction

endclass : cva6_instr_base_test_c
3 changes: 3 additions & 0 deletions verif/env/corev-dv/cva6_instr_gen_config.sv
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
bit enable_rdrs1_hazard;
bit enable_rdrs2_hazard;
bit enable_same_reg;
int unsupported_instr_ratio;

constraint hazard_reg_c {
if (enable_same_reg) {
Expand All @@ -43,6 +44,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
`uvm_field_int(enable_rdrs1_hazard, UVM_DEFAULT)
`uvm_field_int(enable_rdrs2_hazard, UVM_DEFAULT)
`uvm_field_int(enable_same_reg, UVM_DEFAULT)
`uvm_field_int(unsupported_instr_ratio, UVM_DEFAULT)
`uvm_object_utils_end

function new (string name = "");
Expand All @@ -51,6 +53,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
get_bool_arg_value("+enable_rdrs1_hazard=", enable_rdrs1_hazard);
get_bool_arg_value("+enable_rdrs2_hazard=", enable_rdrs2_hazard);
get_bool_arg_value("+enable_same_reg=", enable_same_reg);
get_int_arg_value("+unsupported_instr_ratio=", unsupported_instr_ratio);
endfunction

endclass : cva6_instr_gen_config_c
90 changes: 90 additions & 0 deletions verif/env/corev-dv/cva6_instr_sequence.sv
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/*
* Copyright 2018 Google LLC
* Copyright 2023 Thales DIS
* 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.
*/

//-----------------------------------------------------------------------------------------
// CVA6 instruction sequence, this add unsupported extension instructions to
// hint/illegal instructions
//-----------------------------------------------------------------------------------------

class cva6_instr_sequence_c extends riscv_instr_sequence;

cva6_instr_gen_config_c cfg_cva6; // Configuration class handle
cva6_unsupported_instr_c unsupported_instr; // unsupported instruction generator
int unsupported_instr_pct; // Percentage of unsupported instruction

`uvm_object_utils(cva6_instr_sequence_c)

function new (string name = "");
super.new(name);
unsupported_instr = cva6_unsupported_instr_c::type_id::create("unsupported_instr");
endfunction

function void insert_unsupported_instr();
int bin_instr_cnt;
int idx;
string str;
`DV_CHECK_FATAL($cast(cfg_cva6, cfg), "Could not cast cfg into cfg_cva6")
bin_instr_cnt = instr_cnt * cfg_cva6.unsupported_instr_ratio / 1000;
if (bin_instr_cnt >= 0) begin
`uvm_info(`gfn, $sformatf("Injecting %0d unsupported instructions, ratio %0d/100",
bin_instr_cnt, cfg_cva6.unsupported_instr_ratio), UVM_LOW)
repeat (bin_instr_cnt) begin
`DV_CHECK_RANDOMIZE_WITH_FATAL(unsupported_instr,
unsupported_instr inside {rv64i_instr,rv64c_instr,rv64m_instr,rvfdq_instr};)
str = {indent, $sformatf(".4byte 0x%s # %0s",
unsupported_instr.get_bin_str(), unsupported_instr.comment)};
idx = $urandom_range(0, instr_string_list.size());
instr_string_list.insert(idx, str);
end
end
endfunction

// Convert the instruction stream to the string format.
// Label is attached to the instruction if available, otherwise attach proper space to make
// the code indent consistent.
function void generate_unsupported_instr_stream(bit no_label = 1'b0);
string prefix, str;
int i;
instr_string_list = {};
for(i = 0; i < instr_stream.instr_list.size(); i++) begin
if(i == 0) begin
if (no_label) begin
prefix = format_string(" ", LABEL_STR_LEN);
end else begin
prefix = format_string($sformatf("%0s:", label_name), LABEL_STR_LEN);
end
instr_stream.instr_list[i].has_label = 1'b1;
end else begin
if(instr_stream.instr_list[i].has_label) begin
prefix = format_string($sformatf("%0s:", instr_stream.instr_list[i].label),
LABEL_STR_LEN);
end else begin
prefix = format_string(" ", LABEL_STR_LEN);
end
end
str = {prefix, instr_stream.instr_list[i].convert2asm()};
instr_string_list.push_back(str);
end
insert_unsupported_instr();
insert_illegal_hint_instr();
prefix = format_string($sformatf("%0d:", i), LABEL_STR_LEN);
if(!is_main_program) begin
generate_return_routine(prefix);
end
endfunction

endclass
2 changes: 2 additions & 0 deletions verif/env/corev-dv/cva6_instr_test_pkg.sv
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,9 @@ package cva6_instr_test_pkg;
import cva6_signature_pkg::*;

`include "cva6_instr_gen_config.sv"
`include "cva6_unsupported_instr.sv"
`include "cva6_reg_hazard_stream.sv"
`include "cva6_instr_sequence.sv"
`include "cva6_asm_program_gen.sv"
`include "cva6_instr_base_test.sv"
`include "cva6_instr_hazard_test.sv"
Expand Down
17 changes: 17 additions & 0 deletions verif/env/corev-dv/cva6_reg_hazard_stream.sv
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
/*
* Copyright 2018 Google LLC
* Copyright 2023 Thales DIS
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

// class for hazard instruction stream (RAW)
// that means destination register of previous instruction is the same source register of the current instruction

Expand Down
Loading

0 comments on commit 9974ac1

Please sign in to comment.