diff --git a/verif/env/corev-dv/cva6_asm_program_gen.sv b/verif/env/corev-dv/cva6_asm_program_gen.sv index 9fb9e2e451e..ffd193238f3 100644 --- a/verif/env/corev-dv/cva6_asm_program_gen.sv +++ b/verif/env/corev-dv/cva6_asm_program_gen.sv @@ -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)), diff --git a/verif/env/corev-dv/cva6_instr_base_test.sv b/verif/env/corev-dv/cva6_instr_base_test.sv index 951378e70ca..a3cdb57f937 100644 --- a/verif/env/corev-dv/cva6_instr_base_test.sv +++ b/verif/env/corev-dv/cva6_instr_base_test.sv @@ -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 diff --git a/verif/env/corev-dv/cva6_instr_gen_config.sv b/verif/env/corev-dv/cva6_instr_gen_config.sv index 05aab13db62..d0789d3f8cf 100644 --- a/verif/env/corev-dv/cva6_instr_gen_config.sv +++ b/verif/env/corev-dv/cva6_instr_gen_config.sv @@ -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) { @@ -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 = ""); @@ -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 diff --git a/verif/env/corev-dv/cva6_instr_sequence.sv b/verif/env/corev-dv/cva6_instr_sequence.sv new file mode 100644 index 00000000000..319ce2ec351 --- /dev/null +++ b/verif/env/corev-dv/cva6_instr_sequence.sv @@ -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 diff --git a/verif/env/corev-dv/cva6_instr_test_pkg.sv b/verif/env/corev-dv/cva6_instr_test_pkg.sv index 60192265635..c12ddece751 100644 --- a/verif/env/corev-dv/cva6_instr_test_pkg.sv +++ b/verif/env/corev-dv/cva6_instr_test_pkg.sv @@ -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" diff --git a/verif/env/corev-dv/cva6_reg_hazard_stream.sv b/verif/env/corev-dv/cva6_reg_hazard_stream.sv index 0b4ff473c55..ff5cd32f8fb 100644 --- a/verif/env/corev-dv/cva6_reg_hazard_stream.sv +++ b/verif/env/corev-dv/cva6_reg_hazard_stream.sv @@ -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 diff --git a/verif/env/corev-dv/cva6_unsupported_instr.sv b/verif/env/corev-dv/cva6_unsupported_instr.sv new file mode 100644 index 00000000000..5bea8ac5a4d --- /dev/null +++ b/verif/env/corev-dv/cva6_unsupported_instr.sv @@ -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