CVA6-DV : Add unsupported extension instruction for the embedded config (#1472)

This commit is contained in:
Jalali 2023-09-26 21:04:14 +01:00 committed by GitHub
parent 1683c818c4
commit 0f4212eb2e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 749 additions and 42 deletions

View file

@ -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;
@ -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
@ -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)),
@ -286,14 +395,43 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
end
endfunction
// ECALL trap handler
virtual function void gen_ecall_handler(int hart);
// Illegal instruction trap handler
virtual function void gen_illegal_instr_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, ECALL_EXCEPTION);
string str = format_string("illegal_instr_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, illegal_instr_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
instr.push_back("mret");
gen_section(get_label("illegal_instr_handler", hart), instr);
endfunction
// ECALL trap handler
virtual function void gen_ecall_handler(int hart);
string instr[$];
string str = format_string("ecall_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, ecall_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -304,11 +442,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
// TODO: handshake correct csr based on delegation
virtual function void gen_instr_fault_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, INSTR_FAULT_EXCEPTION);
string str = format_string("instr_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, instr_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -319,11 +464,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
// TODO: handshake correct csr based on delegation
virtual function void gen_load_fault_handler(int hart);
string instr[$];
string str = format_string("load_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, load_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -334,11 +486,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
// TODO: handshake correct csr based on delegation
virtual function void gen_store_fault_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, STORE_FAULT_EXCEPTION);
string str = format_string("store_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, store_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -348,11 +507,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_pt_instr_fault_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, INSTR_PAGE_FAULT_EXCEPTION);
string str = format_string("pt_instr_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, pt_instr_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -362,11 +528,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_pt_load_fault_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, LOAD_PAGE_FAULT_EXCEPTION);
string str = format_string("pt_load_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, pt_load_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -376,11 +549,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_pt_store_fault_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, STORE_PAGE_FAULT_EXCEPTION);
string str = format_string("pt_store_fault_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, pt_store_fault_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -390,11 +570,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_load_misaligned_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, LD_MISALIGNED_EXCEPTION);
string str = format_string("load_misaligned_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, load_misaligned_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -404,11 +591,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_store_misaligned_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, ST_MISALIGNED_EXCEPTION);
string str = format_string("store_misaligned_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, store_misaligned_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);
@ -418,11 +612,18 @@ class cva6_asm_program_gen_c extends riscv_asm_program_gen;
virtual function void gen_instr_misaligned_handler(int hart);
string instr[$];
gen_signature_handshake(instr, CORE_STATUS, INSTR_MISALIGNED_EXCEPTION);
string str = format_string("instr_misaligned_handler_incr_mepc2:", LABEL_STR_LEN);
gen_signature_handshake(instr, CORE_STATUS, LOAD_FAULT_EXCEPTION);
gen_signature_handshake(.instr(instr), .signature_type(WRITE_CSR), .csr(MCAUSE));
instr = {instr,
$sformatf("csrr x%0d, mepc", cfg.gpr[0]),
$sformatf("addi x%0d, x%0d, 4", cfg.gpr[0], cfg.gpr[0]),
$sformatf("lbu x%0d, 0(x%0d)", cfg.gpr[3],cfg.gpr[0]),
$sformatf("li x%0d, 0x3", cfg.gpr[2]),
$sformatf("and x%0d, x%0d, x%0d", cfg.gpr[3], cfg.gpr[3], cfg.gpr[2]),
$sformatf("bne x%0d, x%0d, instr_misaligned_handler_incr_mepc2", cfg.gpr[3], cfg.gpr[2]),
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
str,
$sformatf("addi x%0d, x%0d, 2", cfg.gpr[0], cfg.gpr[0]),
$sformatf("csrw mepc, x%0d", cfg.gpr[0])
};
pop_gpr_from_kernel_stack(MSTATUS, MSCRATCH, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr);

View file

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

View file

@ -31,6 +31,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
bit enable_rdrs2_hazard;
bit enable_same_reg;
bit enable_zicond_extension;
int unsupported_instr_ratio;
constraint hazard_reg_c {
if (enable_same_reg) {
@ -45,6 +46,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
`uvm_field_int(enable_rdrs2_hazard, UVM_DEFAULT)
`uvm_field_int(enable_same_reg, UVM_DEFAULT)
`uvm_field_int(enable_zicond_extension, UVM_DEFAULT)
`uvm_field_int(unsupported_instr_ratio, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "");
@ -54,6 +56,7 @@ class cva6_instr_gen_config_c extends riscv_instr_gen_config;
get_bool_arg_value("+enable_rdrs2_hazard=", enable_rdrs2_hazard);
get_bool_arg_value("+enable_same_reg=", enable_same_reg);
get_bool_arg_value("+enable_zicond_extension=", enable_zicond_extension);
get_int_arg_value("+unsupported_instr_ratio=", unsupported_instr_ratio);
endfunction
endclass : cva6_instr_gen_config_c

View file

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

View file

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

View file

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

View file

@ -0,0 +1,386 @@
/*
* Copyright 2019 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.
*/
// ---------------------------------------------------------------------------------------------
// This class is used to generate instruction in extension that do not supported by a given configuration.
// The illegal instruction will be generated in binary format and mixed with other valid instr.
// The mixed instruction stream will be stored in data section and loaded to instruction pages
// at run time.
// ---------------------------------------------------------------------------------------------
class cva6_unsupported_instr_c extends uvm_object;
string comment;
typedef enum bit [2:0] {
rv64i_instr,
rv64c_instr,
rv64m_instr,
rvfdq_instr
} illegal_ext_instr_type_e;
// Default legal opcode for RV32I instructions
bit [6:0] legal_rv64i_opcode[$] = '{7'b0000011,
7'b0100011,
7'b0011011,
7'b0111011};
bit [6:0] legal_rvfdq_opcode[$] = '{7'b0000111,
7'b0100111,
7'b1000011,
7'b1000111,
7'b1001011,
7'b1001111,
7'b1010011};
bit [6:0] legal_rvfdq_func7[$] = '{7'b0000000,
7'b0000100,
7'b0001000,
7'b0001100,
7'b0101100,
7'b0010000,
7'b1010000,
7'b1101000,
7'b1111000,
7'b0010100,
7'b1100000,
7'b1110000,
7'b0000001,
7'b0000101,
7'b0001001,
7'b0001101,
7'b0101101,
7'b0010001,
7'b0010101,
7'b0100000,
7'b0100001,
7'b1010001,
7'b1110001,
7'b1100001,
7'b1101001,
7'b1111001,
7'b0000011,
7'b0000111,
7'b0001011,
7'b0001111,
7'b0101111,
7'b0010011,
7'b0010111,
7'b0100011,
7'b1010011,
7'b1110011,
7'b1100011,
7'b1101011};
bit [2:0] legal_func3[$] = '{3'b000,
3'b001,
3'b010,
3'b011,
3'b100};
bit [1:0] legal_func2[$] = '{2'b00,
2'b01,
2'b11};
rand illegal_ext_instr_type_e unsupported_instr;
rand bit [31:0] instr_bin;
rand bit [6:0] opcode;
rand bit [2:0] func3;
rand bit [1:0] func2;
rand bit [6:0] func7;
rand bit has_func3;
rand bit has_func7;
rand bit has_func2;
rand bit compressed;
rand bit [1:0] c_op;
rand bit [2:0] c_msb;
cva6_instr_gen_config_c cfg_cva6;
constraint exception_dist_c {
unsupported_instr dist {
rv64i_instr := 1,
rv64c_instr := 1,
rv64m_instr := 1,
rvfdq_instr := 1
};
}
constraint instr_bit_assignment_c {
solve opcode before instr_bin;
solve func3 before instr_bin;
solve func2 before instr_bin;
solve func7 before instr_bin;
solve c_msb before instr_bin;
solve c_op before instr_bin;
if (compressed) {
instr_bin[1:0] == c_op;
instr_bin[15:13] == c_msb;
} else {
instr_bin[6:0] == opcode;
if (has_func7) {
instr_bin[31:25] == func7;
}
if (has_func3) {
instr_bin[14:12] == func3;
}
if (has_func2) {
instr_bin[26:25] == func3;
}
}
}
// RV64I instructions
constraint rv64i_instr_c {
if (unsupported_instr == rv64i_instr) {
compressed == 0;
opcode inside {legal_rv64i_opcode};
func3 inside {3'b110, 3'b011, 3'b001, 3'b101, 3'b0};
if (opcode == 7'b0000011) {
func3 inside {3'b110, 3'b011};
}
if (opcode == 7'b0100011) {
func3 == 3'b011;
}
if (opcode == 7'b0011011) {
func3 inside {3'b101, 3'b001, 3'b0};
if (func3 == 3'b101) {
func7 inside {7'b0, 7'b0100000};
}
if (func3 == 3'b001) {
func7 == 7'b0;
}
}
if (opcode == 7'b0111011) {
func3 inside {3'b101, 3'b001, 3'b0};
if (func3 == 3'b0) {
func7 inside {7'b0, 7'b0100000};
}
if (func3 == 3'b001) {
func7 == 7'b0;
}
if (func3 == 3'b101) {
func7 inside {7'b0, 7'b0100000};
}
}
}
}
// RV64M instructions
constraint rv64c_instr_c {
if (unsupported_instr == rv64m_instr) {
compressed == 0;
opcode == 7'b0111011;
func3 inside {3'b100, 3'b110, 3'b000, 3'b101, 3'b111};
func7 == 7'b0000001;
}
}
// RV64C instructions
constraint rv64m_instr_c {
if (unsupported_instr == rv64c_instr) {
compressed == 1;
c_op != 2'b11;
if (c_op == 2'b0) {
!(c_msb inside {3'b0, 3'b010, 3'b110});
}
if (c_op == 2'b01) {
c_msb == 3'b100;
instr_bin[12:10] inside {3'b0, 3'b001, 3'b111};
if (instr_bin[12:10] != 3'b111) {
instr_bin[6:2] == 5'b0;
}
}
if (c_op == 2'b10) {
!(c_msb inside {3'b100, 3'b010, 3'b110});
if (c_msb == 3'b0) {
instr_bin[6:2] == 5'b0;
instr_bin[12] == 1'b0;
}
}
}
}
// RV32FDQ, RV64FDQ instructions
constraint rvfdq_instr_c {
if (unsupported_instr == rvfdq_instr) {
compressed == 0;
opcode inside {legal_rvfdq_opcode};
func7 inside {legal_rvfdq_func7};
if (opcode == 7'b0000111) {
func3 inside {3'b010, 3'b011, 3'b100};
}
if (opcode == 7'b0100111) {
func3 inside {3'b010, 3'b011, 3'b100};
}
if (opcode == 7'b1000011) {
func2 inside {legal_func2};
}
if (opcode == 7'b1000111) {
func2 inside {legal_func2};
}
if (opcode == 7'b1001011) {
func2 inside {legal_func2};
}
if (opcode == 7'b1001111) {
func2 inside {legal_func2};
}
if (opcode == 7'b1010011) {
func3 inside {3'b0, 3'b010, 3'b001};
if (func3 == 3'b0) {
func7 inside {7'b0010000,7'b0010100,7'b1110000,7'b1010000,
7'b1111000,7'b0010001,7'b0010101,7'b1010001,
7'b1110001,7'b1111001,7'b0010011,7'b0010111,
7'b1010011};
if (func7 == 7'b1110000) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1111000) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1110001) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1111001) {
instr_bin[24:20] == 5'b0;
}
}
if (func3 == 3'b001) {
func7 inside {7'b0010000,7'b0010100,7'b1110000,7'b1010000,
7'b0010001,7'b0010101,7'b1010001,7'b1110001,
7'b1010011,7'b1110011,7'b0010011,7'b0010111};
if (func7 == 7'b1110000) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1110001) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1110011) {
instr_bin[24:20] == 5'b0;
}
}
if (func3 == 3'b010) {
func7 inside {7'b0010000,7'b1010000,7'b0010001,7'b1010001,
7'b0010011,7'b1010011};
}
if (func7 == 7'b0101100) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b1100000) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
if (func7 == 7'b1101000) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
if (func7 == 7'b0101101) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b0100000) {
instr_bin[24:20] inside {5'b00001, 5'b00011};
}
if (func7 == 7'b0100001) {
instr_bin[24:20] inside {5'b0, 5'b00011};
}
if (func7 == 7'b1100001) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
if (func7 == 7'b1101001) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
if (func7 == 7'b0101111) {
instr_bin[24:20] == 5'b0;
}
if (func7 == 7'b0100011) {
instr_bin[24:20] inside {5'b0, 5'b00001};
}
if (func7 == 7'b1100011) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
if (func7 == 7'b1101011) {
instr_bin[24:20] inside {5'b0, 5'b00001, 5'b00010, 5'b00011};
}
}
}
}
constraint has_func7_c {
solve opcode before func7;
solve func7 before func3;
if (opcode == 7'b0111011) {
has_func3 == 1'b1;
has_func7 == 1'b1;
has_func2 == 1'b0;
}
if (opcode inside {legal_rv64i_opcode}) {
has_func3 == 1'b1;
has_func2 == 1'b0;
if (((opcode == 7'b0011011) && (func3 == 3'b000)) ||
(opcode inside {7'b0000011, 7'b0100011})) {
has_func7 == 1'b0;
} else {
has_func7 == 1'b1;
}
}
if (opcode inside {7'b0000111, 7'b0100111}) {
has_func2 == 1'b0;
has_func3 == 1'b1;
has_func7 == 1'b0;
}
if (opcode inside {7'b1000011, 7'b1000111, 7'b1001011, 7'b1001111}) {
has_func2 == 1'b1;
has_func3 == 1'b0;
has_func7 == 1'b0;
}
if (opcode == 7'b1010011) {
has_func2 == 1'b0;
has_func7 == 1'b1;
if (func7 inside {7'b0010000, 7'b0010100, 7'b1110000, 7'b1010000, 7'b1110000, 7'b1111000,
7'b0010001, 7'b0010101, 7'b1010001, 7'b1110001, 7'b1111001, 7'b0010011,
7'b0010011, 7'b0010111, 7'b1010011, 7'b1110011}) {
has_func3 == 1'b1;
} else {
has_func3 == 1'b0;
}
}
}
`uvm_object_utils(cva6_unsupported_instr_c)
`uvm_object_new
function string get_bin_str();
if (compressed) begin
get_bin_str = $sformatf("%4h", instr_bin[15:0]);
end else begin
get_bin_str = $sformatf("%8h", instr_bin[31:0]);
end
`uvm_info(`gfn, $sformatf("unsupported extension instruction type: %0s, unsupported instruction: 0x%0x",
unsupported_instr.name(), instr_bin), UVM_HIGH)
endfunction
function void post_randomize();
comment = unsupported_instr.name();
if (unsupported_instr == rv64i_instr) begin
comment = {comment};
end else if (unsupported_instr == rv64m_instr) begin
comment = {comment};
end else if (unsupported_instr == rvfdq_instr) begin
comment = {comment};
end else if (unsupported_instr == rv64c_instr) begin
comment = {comment};
end
endfunction
endclass