diff --git a/vendor/google_riscv-dv.lock.hjson b/vendor/google_riscv-dv.lock.hjson index 7c238a76..446124a8 100644 --- a/vendor/google_riscv-dv.lock.hjson +++ b/vendor/google_riscv-dv.lock.hjson @@ -9,6 +9,6 @@ upstream: { url: https://github.com/google/riscv-dv - rev: 3cf691dcb96f2cd72250690216b60f2b0c0ac804 + rev: 61755c001bec0433fb69458f74d95476d2101cf3 } } diff --git a/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr.py b/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr.py index d38d96f9..7c69763c 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr.py @@ -19,7 +19,7 @@ import os import vsc from collections import defaultdict from bitstring import BitArray -from pygen_src.riscv_instr_pkg import pkg_ins, riscv_reg_t +from pygen_src.riscv_instr_pkg import pkg_ins, riscv_reg_t, riscv_instr_name_t from pygen_src.isa import rv32i_instr # NOQA from pygen_src.target.rv32i import riscv_core_setting as rcs @@ -271,14 +271,31 @@ class riscv_instr: sign = (self.imm & 0x80000000) >> 31 self.imm = self.imm >> (32 - self.imm_len) & self.shift_t # Signed extension - if((sign and not(self.format == "U_FORMAT")) or (self.imm_type in ["UIMM", "NZUIMM"])): + if(sign and not((self.format.name == "U_FORMAT") or + (self.imm_type.name in ["UIMM", "NZUIMM"]))): self.imm = self.imm_mask | self.imm + def imm_c(self): + imm_t = BitArray(uint = self.imm, length = 32) + if self.instr_name in [riscv_instr_name_t.SLLIW.name, riscv_instr_name_t.SRLIW.name, + riscv_instr_name_t.SRAIW.name]: + imm_t[20:27:1] = 0 + self.imm = imm_t.uint + if self.instr_name in [riscv_instr_name_t.SLLI.name, riscv_instr_name_t.SRLI.name, + riscv_instr_name_t.SRAI.name]: + if rcs.XLEN == 32: + imm_t[20:27:1] = 0 + self.imm = imm_t.uint + else: + self.imm_t[20:26:1] = 0 + self.imm = imm_t.uint + def post_randomize(self): + self.imm_c() self.extend_imm() self.update_imm_str() - def convert2asm(self, prefix=" "): + def convert2asm(self, prefix = " "): asm_str = pkg_ins.format_string(string = self.get_instr_name(), length = pkg_ins.MAX_INSTR_STR_LEN) if(self.category.name != "SYSTEM"): diff --git a/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr_cov.py b/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr_cov.py new file mode 100644 index 00000000..2bbe2349 --- /dev/null +++ b/vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr_cov.py @@ -0,0 +1,46 @@ +import sys +import vsc +import logging +from enum import Enum, auto +from pygen_src.riscv_instr_pkg import riscv_reg_t + +class operand_sign_e(Enum): + POSITIVE = 0 + NEGATIVE = auto() + +class div_result_e(Enum): + DIV_NORMAL = 0 + DIV_BY_ZERO = auto() + DIV_OVERFLOW = auto() + +class compare_result_e(Enum): + EQUAL = 0 + LARGER = auto() + SMALLER = auto() + +class logical_similarity_e(Enum): + IDENTICAL = 0 + OPPOSITE = auto() + SIMILAR = auto() + DIFFERENT = auto() + +class special_val_e(Enum): + NORMAL_VAL = 0 + MIN_VAL = auto() + MAX_VAL = auto() + ZERO_VAL = auto() + + +def get_gpr(reg_name): + reg_name = reg_name.upper() + if not reg_name in riscv_reg_t: + logging.fatal("Cannot convert {} to GPR".format(reg_name)) + return riscv_reg_t[reg_name] + +def get_gpr_state(reg_name): + if reg_name in ["zero", "x0"]: + return 0 + elif reg_name in gpr_state: + return gpr_state[reg_name] + else: + logging.warning("Cannot find GPR state: {}".format(reg_name)) diff --git a/vendor/google_riscv-dv/pygen/pygen_src/riscv_asm_program_gen.py b/vendor/google_riscv-dv/pygen/pygen_src/riscv_asm_program_gen.py index ff1fbcb6..054bdc29 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/riscv_asm_program_gen.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/riscv_asm_program_gen.py @@ -17,7 +17,7 @@ import logging import random from bitstring import BitArray from pygen_src.riscv_instr_sequence import riscv_instr_sequence -from pygen_src.riscv_instr_pkg import (pkg_ins, privileged_reg_t) +from pygen_src.riscv_instr_pkg import pkg_ins, privileged_reg_t, privileged_mode_t, mtvec_mode_t from pygen_src.riscv_instr_gen_config import cfg from pygen_src.target.rv32i import riscv_core_setting as rcs ''' @@ -103,9 +103,31 @@ class riscv_asm_program_gen: logging.info("Main/sub program generation...done") # program end self.gen_program_end(hart) + for hart in range(cfg.num_of_harts): + self.gen_data_page_begin(hart) + if(cfg.no_data_page): + self.gen_data_page() + + if((hart == 0) and ("RV32A" in rcs.supported_isa)): + self.gen_data_page(hart, amo = 1) + + self.gen_stack_section(hart) + if(not cfg.bare_program_mode): + self.gen_kernel_sections(hart) def gen_kernel_sections(self, hart): - pass + if(rcs.SATP_MODE != "BARE"): + self.instr_stream.append(".align 12") + else: + self.instr_stream.append(".align 2") + self.instr_stream.append(pkg_ins.get_label("kernel_instr_start:", hart)) + self.instr_stream.append(".text") + + self.gen_all_trap_handler(hart) + for mode in rcs.supported_privileged_mode: + self.gen_interrupt_handler_section(mode, hart) + + self.gen_kernel_stack_section(hart) def gen_kernel_program(self, hart, seq): pass @@ -146,16 +168,57 @@ class riscv_asm_program_gen: self.gen_section("_exit", ["j write_tohost"]) def gen_data_page_begin(self, hart): - pass + self.instr_stream.append(".section .data") + if (hart == 0): + self.instr_stream.append(".align 6; .global tohost; tohost: .dword 0;") + self.instr_stream.append(".align 6; .global fromhost; fromhost: .dword 0;") def gen_data_page(self, hart, is_kernel = 0, amo = 0): pass def gen_stack_section(self, hart): - pass + hart_prefix_string = pkg_ins.hart_prefix(hart) + if(cfg.use_push_data_section): + self.instr_stream.append( + ".pushsection .{}user_stack,\"aw\",@progbits;".format(hart_prefix_string)) + else: + self.instr_stream.append( + ".section .{}user_stack,\"aw\",@progbits;".format(hart_prefix_string)) + if(rcs.SATP_MODE != "BARE"): + self.instr_stream.append(".align 12") + else: + self.instr_stream.append(".align 2") + + self.instr_stream.append(pkg_ins.get_label("user_stack_start:", hart)) + self.instr_stream.append(".rept {}".format(cfg.kernel_stack_len - 1)) + self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) + self.instr_stream.append(".endr") + self.instr_stream.append(pkg_ins.get_label("user_stack_end:", hart)) + self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) + if (cfg.use_push_data_section): + self.instr_stream.push_back(".popsection;") def gen_kernel_stack_section(self, hart): - pass + hart_prefix_string = pkg_ins.hart_prefix(hart) + if(cfg.use_push_data_section): + self.instr_stream.append( + ".pushsection .{}kernel_stack,\"aw\",@progbits;".format(hart_prefix_string)) + else: + self.instr_stream.append( + ".section .{}kernel_stack,\"aw\",@progbits;".format(hart_prefix_string)) + if(rcs.SATP_MODE != "BARE"): + self.instr_stream.append(".align 12") + else: + self.instr_stream.append(".align 2") + + self.instr_stream.append(pkg_ins.get_label("kernel_stack_start:", hart)) + self.instr_stream.append(".rept {}".format(cfg.kernel_stack_len - 1)) + self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) + self.instr_stream.append(".endr") + self.instr_stream.append(pkg_ins.get_label("kernel_stack_end:", hart)) + self.instr_stream.append(".{}byte 0x0".format(rcs.XLEN // 8)) + if (cfg.use_push_data_section): + self.instr_stream.push_back(".popsection;") def gen_init_section(self, hart): string = pkg_ins.format_string("init:", pkg_ins.LABEL_STR_LEN) @@ -246,8 +309,8 @@ class riscv_asm_program_gen: if(cfg.virtual_addr_translation_on): self.page_table_list.process_page_table(instr) self.gen_section(pkg_ins.get_label("process_pt", hart), instr) - - self.setup_epc(hart) + # Commenting for now + # self.setup_epc(hart) if(rcs.support_pmp): self.gen_privileged_mode_switch_routine(hart) @@ -321,14 +384,46 @@ class riscv_asm_program_gen: self.gen_section(pkg_ins.get_label("trap_vec_init", hart), instr) def gen_all_trap_handler(self, hart): - pass + if(not rcs.support_pmp): + self.gen_trap_handlers(hart) + self.gen_ecall_handler(hart) + self.gen_instr_fault_handler(hart) + self.gen_load_fault_handler(hart) + self.gen_store_fault_handler(hart) def gen_trap_handlers(self, hart): - pass + self.gen_trap_handler_section(hart, "m", privileged_reg_t.MCAUSE, + privileged_reg_t.MTVEC, privileged_reg_t.MTVAL, + privileged_reg_t.MEPC, privileged_reg_t.MSCRATCH, + privileged_reg_t.MSTATUS, privileged_reg_t.MIE, + privileged_reg_t.MIP) def gen_trap_handler_section(self, hart, mode, cause, tvec, tval, epc, scratch, status, ie, ip): - pass + is_interrupt = 1 + tvec_name = "" + instr = [] + if (cfg.mtvec_mode == mtvec_mode_t.VECTORED): + self.gen_interrupt_vector_table(hart, mode, status, cause, ie, ip, scratch, instr) + else: + # Push user mode GPR to kernel stack before executing exception handling, + # this is to avoid exception handling routine modify user program state + # unexpectedly + # TODO + pkg_ins.push_gpr_to_kernel_stack( + status, scratch, cfg.mstatus_mprv, cfg.sp, cfg.tp, instr) + # The trap handler will occupy one 4KB page, it will be allocated one entry in + # the page table with a specific privileged mode. + + if (rcs.SATP_MODE != "BARE"): + self.instr_stream.append(".align 12") + else: + self.instr_stream.append(".align {}".format(cfg.tvec_alignment)) + + tvec_name = tvec.name + self.gen_section(pkg_ins.get_label("{}_handler".format(tvec_name.lower()), hart), instr) + + # TODO Exception handler def gen_interrupt_vector_table(self, hart, mode, status, cause, ie, ip, scratch, instr): @@ -362,7 +457,74 @@ class riscv_asm_program_gen: pass def gen_interrupt_handler_section(self, mode, hart): - pass + interrupt_handler_instr = [] + ls_unit = "w" if (rcs.XLEN == 32) else "d" + # TODO + # if(mode.value < cfg.init_privileged_mode): + # return + if(mode is privileged_mode_t.USER_MODE.name and not (rcs.support_umode_trap)): + return + if(mode == privileged_mode_t.MACHINE_MODE.name): + mode_prefix = "m" + status = privileged_reg_t.MSTATUS + ip = privileged_reg_t.MIP + ie = privileged_reg_t.MIE + scratch = privileged_reg_t.MSCRATCH + elif(mode is privileged_mode_t.SUPERVISOR_MODE.name): + mode_prefix = "s" + status = privileged_reg_t.SSTATUS + ip = privileged_reg_t.SIP + ie = privileged_reg_t.SIE + scratch = privileged_reg_t.SSCRATCH + elif(mode == privileged_mode_t.USER_MODE.name): + mode_prefix = "u" + status = privileged_reg_t.USTATUS + ip = privileged_reg_t.UIP + ie = privileged_reg_t.UIE + scratch = privileged_reg_t.USCRATCH + else: + logging.critical("Unsupported mode: %0s" % (mode)) + + if(cfg.enable_nested_interrupt): + interrupt_handler_instr.append("csrr x%0d, 0x%0x" % (cfg.gpr[0].value, scratch.value)) + interrupt_handler_instr.append("bgtz x%0d, 1f" % (cfg.gpr[0].value)) + interrupt_handler_instr.append("csrwi 0x%0x, 0x1" % (scratch.value)) + + if(status == privileged_reg_t.MSTATUS): + interrupt_handler_instr.append("csrsi 0x%0x, 0x%0x" % (status.value, 8)) + elif(status == privileged_reg_t.SSTATUS): + interrupt_handler_instr.append("csrsi 0x%0x, 0x%0x" % (status.value, 2)) + elif(status == privileged_reg_t.USTATUS): + interrupt_handler_instr.append("csrsi 0x%0x, 0x%0x" % (status.value, 1)) + else: + logging.critical("Unsupported status %0s" % (status.value)) + + interrupt_handler_instr.append("1: csrwi 0x%0x,0" % (scratch.value)) + + to_extend_interrupt_hanlder_instr = ["csrr x%0d, 0x%0x # %0s;" % (cfg.gpr[0].value, + status.value, + status.name), + "csrr x%0d, 0x%0x # %0s;" % (cfg.gpr[0].value, + ie.value, ie.name), + "csrr x%0d, 0x%0x # %0s;" % (cfg.gpr[0].value, + ip.value, ip.name), + "csrrc x%0d, 0x%0x, x%0d # %0s;" % (cfg.gpr[0].value, + ip.value, + cfg.gpr[0].value, + ip.name)] + interrupt_handler_instr.extend(to_extend_interrupt_hanlder_instr) + self.gen_plic_section(interrupt_handler_instr) + pkg_ins.pop_gpr_from_kernel_stack(status.name, scratch.name, cfg.mstatus_mprv, + cfg.sp, cfg.tp, interrupt_handler_instr) + interrupt_handler_instr.append("%0sret;" % (mode_prefix)) + + if(rcs.SATP_MODE != "BARE"): + self.instr_stream.append(".align 12") + else: + self.instr_stream.append(".align 2") + + self.gen_section(pkg_ins.get_label("%0smode_intr_handler" % + (mode_prefix), hart), interrupt_handler_instr) def format_section(self, instr): pass diff --git a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_gen_config.py b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_gen_config.py index 1a4b5d03..0f56094a 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_gen_config.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_gen_config.py @@ -63,7 +63,14 @@ class riscv_instr_gen_config: self.fcsr_rm = list(map(lambda csr_rm: csr_rm.name, f_rounding_mode_t)) self.enable_sfence = 0 - self.gpr = vsc.rand_list_t(vsc.enum_t(riscv_reg_t), sz =4) + self.gpr = [] + + # Helper fields for gpr + self.gpr0 = vsc.rand_enum_t(riscv_reg_t) + self.gpr1 = vsc.rand_enum_t(riscv_reg_t) + self.gpr2 = vsc.rand_enum_t(riscv_reg_t) + self.gpr3 = vsc.rand_enum_t(riscv_reg_t) + self.scratch_reg = vsc.rand_enum_t(riscv_reg_t) self.pmp_reg = vsc.rand_enum_t(riscv_reg_t) self.sp = vsc.rand_enum_t(riscv_reg_t) @@ -154,7 +161,15 @@ class riscv_instr_gen_config: @vsc.constraint def gpr_c(self): - pass # TODO + self.gpr0.not_inside(vsc.rangelist(self.sp, self.tp, self.scratch_reg, self.pmp_reg, + riscv_reg_t.ZERO, riscv_reg_t.RA, riscv_reg_t.GP)) + self.gpr1.not_inside(vsc.rangelist(self.sp, self.tp, self.scratch_reg, self.pmp_reg, + riscv_reg_t.ZERO, riscv_reg_t.RA, riscv_reg_t.GP)) + self.gpr2.not_inside(vsc.rangelist(self.sp, self.tp, self.scratch_reg, self.pmp_reg, + riscv_reg_t.ZERO, riscv_reg_t.RA, riscv_reg_t.GP)) + self.gpr3.not_inside(vsc.rangelist(self.sp, self.tp, self.scratch_reg, self.pmp_reg, + riscv_reg_t.ZERO, riscv_reg_t.RA, riscv_reg_t.GP)) + vsc.unique(self.gpr0, self.gpr1, self.gpr2, self.gpr3) def check_setting(self): support_64b = 0 @@ -221,6 +236,9 @@ class riscv_instr_gen_config: self.s_mode_interrupt_delegation[j] = 0 def pre_randomize(self): + # Clearing the contents of self.gpr after each randomization. + # As it is being extended in post_randomize function. + self.gpr.clear() for x in rcs.supported_privileged_mode: if(x == "SUPERVISOR_MODE"): self.support_supervisor_mode = 1 @@ -229,6 +247,9 @@ class riscv_instr_gen_config: pass def post_randomize(self): + # Temporary fix for gpr_c constraint. + self.gpr.extend((self.gpr0, self.gpr1, self.gpr2, self.gpr3)) + self.reserved_regs.append(self.tp) self.reserved_regs.append(self.sp) self.reserved_regs.append(self.scratch_reg) @@ -252,7 +273,7 @@ class riscv_instr_gen_config: for mode in self.init_privileged_mode: if mode == "MACHINE_MODE": continue - elif mode == 'SUPERVISOR_MODE': + if mode == 'SUPERVISOR_MODE': invalid_lvl.append('M') logging.info("supr_mode---") logging.debug(invalid_lvl) @@ -295,8 +316,10 @@ def parse_args(): parse.add_argument('--no_ebreak', help = 'no_ebreak', choices = [0, 1], type = int, default = 1) parse.add_argument('--no_dret', help = 'no_dret', choices = [0, 1], type = int, default = 1) parse.add_argument('--no_wfi', help = 'no_wfi', choices = [0, 1], type = int, default = 1) + + # TODO : Enabling no_branch_jump default to 1 for now. parse.add_argument('--no_branch_jump', help = 'no_branch_jump', - choices = [0, 1], type = int, default = 0) + choices = [0, 1], type = int, default = 1) parse.add_argument('--no_load_store', help = 'no_load_store', choices = [0, 1], type = int, default = 0) parse.add_argument('--no_csr_instr', help = 'no_csr_instr', diff --git a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_pkg.py b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_pkg.py index fda01b84..fd4e134d 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_pkg.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_pkg.py @@ -12,6 +12,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. """ +import logging from enum import Enum, auto from bitstring import BitArray from pygen_src.target.rv32i import riscv_core_setting as rcs @@ -1160,6 +1161,23 @@ class all_categories(Enum): INTERRUPT = auto() AMO = auto() +def get_val(in_string, hexa=0): + if len(in_string) > 2: + if "0x" in in_string: + out_val = hex(int(in_string, base=16)) + return out_val + if hexa: + out_val = hex(int(in_string, base=16)) + else: + out_val = int(in_string) + logging.info("riscv_instr_pkg: imm: {} -> {}".format(in_string, out_val)) + return out_val + +class hazard_e(Enum): + NO_HAZARD = 0 + RAW_HAZARD = auto() + WAR_HAZARD = auto() + WAW_HAZARD = auto() class riscv_instr_pkg: def __init__(self): @@ -1191,7 +1209,7 @@ class riscv_instr_pkg: formatted_str = length * " " if (int(length) < len(string)): return string - formatted_str = string + formatted_str[0: (int(length) - len(string) - 1)] + formatted_str = string + formatted_str[0: (int(length) - len(string))] return formatted_str def format_data(self, data, byte_per_group = 4): @@ -1202,5 +1220,11 @@ class riscv_instr_pkg: string = string + f"{hex(data[i])}" return string + def push_gpr_to_kernel_stack(self, status, scratch, mprv, sp, tp, instr): + pass + + def pop_gpr_from_kernel_stack(self, status, scratch, mprv, sp, tp, instr): + pass + pkg_ins = riscv_instr_pkg() diff --git a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_stream.py b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_stream.py index 6b868623..ad4852a9 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_stream.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_stream.py @@ -63,6 +63,7 @@ class riscv_instr_stream: return elif idx > current_instr_cnt or idx < 0: logging.error("Cannot insert instr:%0s at idx %0d", instr.convert2asm(), idx) + sys.exit(1) self.instr_list.insert(idx, instr) def insert_instr_stream(self, new_instr, idx = -1, replace = 0): @@ -128,7 +129,7 @@ class riscv_instr_stream: if len(insert_instr_position) > 0: insert_instr_position.sort() for i in range(new_instr_cnt): - insert_instr_position[i] = random.rangeint(0, current_instr_cnt) + insert_instr_position[i] = random.randint(0, current_instr_cnt) if len(insert_instr_position) > 0: insert_instr_position.sort() if contained: @@ -201,7 +202,7 @@ class riscv_rand_instr_stream(riscv_instr_stream): if len(self.instr_list) == 0: break - def randomize_instr(self, instr, is_in_debug = 0, disable_dist = 0): + def randomize_instr(self, instr, is_in_debug = 0): exclude_instr = [] is_SP_in_reserved_rd = riscv_reg_t.SP in self.reserved_rd is_SP_in_reserved_regs = riscv_reg_t.SP in cfg.reserved_regs diff --git a/vendor/google_riscv-dv/pygen/pygen_src/target/rv32i/riscv_core_setting.py b/vendor/google_riscv-dv/pygen/pygen_src/target/rv32i/riscv_core_setting.py index a3039f65..dbf7a2da 100644 --- a/vendor/google_riscv-dv/pygen/pygen_src/target/rv32i/riscv_core_setting.py +++ b/vendor/google_riscv-dv/pygen/pygen_src/target/rv32i/riscv_core_setting.py @@ -16,7 +16,7 @@ XLEN = 32 implemented_csr = ['MVENDORID', 'MARCHID', 'MIMPID', 'MHARTID', 'MSTATUS', 'MISA', 'MIE', 'MTVEC', 'MCOUNTEREN', 'MSCRATCH', 'MEPC', 'MCAUSE', 'MTVAL', 'MIP'] -SATP_MODE = ['BARE'] +SATP_MODE = 'BARE' supported_isa = ['RV32I'] diff --git a/vendor/google_riscv-dv/pygen/pygen_src/test/out/.gitkeep b/vendor/google_riscv-dv/pygen/pygen_src/test/out/.gitkeep deleted file mode 100644 index e69de29b..00000000 diff --git a/vendor/google_riscv-dv/pygen/pygen_src/test/riscv_instr_cov_test.py b/vendor/google_riscv-dv/pygen/pygen_src/test/riscv_instr_cov_test.py new file mode 100644 index 00000000..8cc37c5b --- /dev/null +++ b/vendor/google_riscv-dv/pygen/pygen_src/test/riscv_instr_cov_test.py @@ -0,0 +1,407 @@ +# Lint as: python3 +"""Tests for riscv_instr_cov.""" +import sys +import os +import logging +import argparse +import vsc # PyVSC library +import csv # Python library to read/write from/to CSV files +from bitstring import BitArray +from pygen.pygen_src.isa.riscv_instr_cov import * +from pygen.pygen_src.riscv_instr_pkg import * +from pygen.pygen_src.target.rv32i import riscv_core_setting as rcs + + + +logging.basicConfig(filename='logging.log',level=logging.DEBUG) + +class riscv_instr(): + """ Class for a riscv instruction; data parsed from the CSV file will fill + different fields of an instruction """ + # class attr. to keep track of reg_name:reg_value throughout the program + gpr_state = {} + def __init__(self, instr_name): + self.pc = 0 # Program counter (PC) of the instruction + self.instr = instr_name + self.gpr = None # destination operand of the instruction + self.csr = None + self.binary = 0 # Instruction binary + self.mode = None # Instruction mode + self.trace = "None" # String representation of the instruction + self.operands = "None" # Instruction operands (srcss/dests) + self.pad = None # Not used + + self.rs1_value = None + self.rs2_value = None + self.rs3_value = None + self.rd_value = None + self.fs1_value = None + self.fs2_value = None + self.fs3_value = None + self.fd_value = None + + self.mem_addr = None + self.unaligned_pc = 0 + self.unaligned_mem_access = 0 + self.compressed = 0 + self.branch_hit = 0 + self.div_result = None + self.rs1_sign = None + self.rs2_sign = None + self.rs3_sign = None + self.fs1_sign = None + self.fs2_sign = None + self.fs3_sign = None + self.imm_sign = None + self.rd_sign = None + self.gpr_hazard = None + self.lsu_hazard = None + self.rs1_special_value = None + self.rs2_special_value = None + self.rs3_special_value = None + self.rd_special_value = None + self.imm_special_value = None + self.compare_result = None + self.logical_similarity = None + + # TODO: add & map... + #self.imm + #self.format + #self.category + + def pre_sample(self): + unaligned_pc = self.pc[-2:] != "00" + self.rs1_sign = self.get_operand_sign(self.rs1_value) + self.rs2_sign = self.get_operand_sign(self.rs2_value) + self.rs3_sign = self.get_operand_sign(self.rs3_value) + self.rd_sign = self.get_operand_sign(self.rd_value) + self.fs1_sign = self.get_operand_sign(self.fs1_value) + self.fs2_sign = self.get_operand_sign(self.fs2_value) + self.fs3_sign = self.get_operand_sign(self.fs3_value) + self.fd_sign = self.get_operand_sign(self.fd_value) + self.imm_sign = self.get_imm_sign(self.imm) + self.rs1_special_value = self.get_operand_special_value(self.rs1_value) + self.rd_special_value = self.get_operand_special_value(self.rd_value) + self.rs2_special_value = self.get_operand_special_value(self.rs2_value) + self.rs3_special_value = self.get_operand_special_value(self.rs3_value) + if (self.format != riscv_instr_format_t.R_FORMAT and + self.format != riscv_instr_format_t.CR_FORMAT): + self.imm_special_value = self.get_imm_special_val(self.imm) + if self.category in [riscv_instr_category_t.COMPARE, + riscv_instr_category_t.BRANCH]: + self.compare_result = self.get_compare_result() + if self.category in [riscv_instr_category_t.LOAD, + riscv_instr_category_t.STORE]: + self.mem_addr = self.rs1_value + self.imm + self.unaligned_mem_access = self.is_unaligned_mem_access() + if self.unaligned_mem_access: + logging.info("Unaligned: {}, mem_addr: {}".format( + self.instr, self.mem_addr)) + if self.category == riscv_instr_category_t.LOGICAL: + self.logical_similarity = self.get_logical_similarity() + if self.category == riscv_instr_category_t.BRANCH: + self.branch_hit = self.is_branch_hit() + #TODO: string > enumeration + if self.instr in ["DIV", "DIVU", "REM", "REMU", "DIVW", "DIVUW", + "REMW", "REMUW"]: + self.div_result = self.get_div_result() + + def get_operand_sign(self, operand): + #TODO: change operand to vsc.bit_t + out = BitArray(int=operand.val, length=rcs.XLEN) + if out[0]: + return operand_sign_e["NEGATIVE"] + else: + return operand_sign_e["POSITIVE"] + + def is_unaligned_mem_access(self): + #TODO: string > enumeration + if (self.instr in ["LWU", "LD", "SD", "C_LD", "C_SD"] and + self.mem_addr % 8 != 0): + return True + elif (self.instr in ["LW", "SW", "C_LW", "C_SW"] and + self.mem_addr % 4 != 0): + return True + elif (self.instr in ["LH", "LHU", "SH"] and + self.mem_addr % 2 != 0): + return True + return False + + def get_imm_sign(self, imm): + #TODO: change imm to vsc.int_t(32) + out = BitArray(int=imm.val, length=rcs.XLEN) + if out[0]: + return operand_sign_e["NEGATIVE"] + else: + return operand_sign_e["POSITIVE"] + + def get_div_result(self): + #TODO: change rs2_value to vsc.int_t(32) + if self.rs2_value.val == 0: + return div_result_e["DIV_BY_ZERO"] + elif self.rs2_value.val == 1 and self.rs1_value.val == (1 << (rcs.XLEN-1)): + return div_result_e["DIV_OVERFLOW"] + else: + return div_result_e["DIV_NORMAL"] + + def get_operand_special_value(self, operand): + if operand.val == 0: + return special_val_e["ZERO_VAL"] + elif operand.val == 1 << (rcs.XLEN-1): + return special_val_e["MIN_VAL"] + elif operand.val == 1 >> 1: + return special_val_e["MAX_VAL"] + else: + return special_val_e["NORMAL_VAL"] + + def get_imm_special_val(self, imm): + if imm.val == 0: + return special_val_e["ZERO_VAL"] + elif self.format == riscv_instr_format_t.U_FORMAT: + # unsigned immediate value + # TODO: self.imm_len + max = vsc.int_t(32, (1 << self.imm_len)-1) + if imm.val == 0: + return special_val_e["MIN_VAL"] + if imm.val == max.val: + return special_val_e["MAX_VAL"] + else: + # signed immediate value + max = vsc.int_t(32, (2 ** (self.imm_len - 1)) - 1) + min = vsc.int_t(32, -2 ** (self.imm_len - 1)) + if min.val == imm.val: + return special_val_e["MIN_VAL"] + if max.val == imm.val: + return special_val_e["MAX_VAL"] + return special_val_e["NORMAL_VAL"] + + def get_compare_result(self): + val1 = vsc.int_t(rcs.XLEN) + val2 = vsc.int_t(rcs.XLEN) + val1.val = self.rs1_value.val + val2.val = self.imm.val if ( + self.format == riscv_instr_format_t.I_FORMAT) \ + else self.rs2_value.val + if val1.val == val2.val: + return compare_result_e["EQUAL"] + elif val1.val < val2.val: + return compare_result_e["SMALLER"] + else: + return compare_result_e["LARGER"] + + def is_branch_hit(self): + # TODO: string/enumeration + if self.instr == "BEQ": + return self.rs1_value.val == self.rs2_value.val + elif self.instr == "C_BEQZ": + return self.rs1_value.val == 0 + elif self.instr == "BNE": + return self.rs1_value.val != self.rs2_value.val + elif self.instr == "C_BNEZ": + return self.rs1_value.val != 0 + elif self.instr == "BLT" or self.instr == "BLTU": + return self.rs1_value.val < self.rs2_value.val + elif self.instr == "BGE" or self.instr == "BGEU": + return self.rs1_value.val >= self.rs2_value.val + else: + logging.error("Unexpected instruction {}".format(self.instr)) + + def get_logical_similarity(self): + val1 = vsc.int_t(rcs.XLEN, self.rs1_value.val) + val2 = vsc.int_t(rcs.XLEN) + val2.val = (self.imm.val if self.format == riscv_instr_format_t.I_FORMAT + else self.rs2_value.val) + temp = bin(val1.val ^ val2.val) + bit_difference = len([[ones for ones in temp[2:] if ones=='1']]) + if val1.val == val2.val: + return logical_similarity_e["IDENTICAL"] + elif bit_difference == 32: + return logical_similarity_e["OPPOSITE"] + elif bit_difference < 5: + return logical_similarity_e["SIMILAR"] + else: + return logical_similarity_e["DIFFERENT"] + + def check_hazard_condition(self, pre_instr): + # TODO: has_rd(), has_rs1, has_rs2, rd, category, convert2asm (from IG) + if pre_instr.has_rd(): + if ((self.has_rs1 and self.rs1 == pre_instr.rd) or + (self.has_rs2 and self.rs1 == pre_instr.rd)): + self.gpr_hazard = hazard_e["RAW_HAZARD"] + elif self.has_rd and self.rd == pre_instr.rd: + self.gpr_hazard = hazard_e["WAW_HAZARD"] + elif (self.has_rd and + ((pre_instr.has_rs1 and (pre_instr.rs1 == self.rd)) or + (pre_instr.has_rs2 and (pre_instr.rs2 == self.rd)))): + self.gpr_hazard = hazard_e["WAR_HAZARD"] + else: + self.gpr_hazard = hazard_e["NO_HAZARD"] + if self.category == riscv_instr_category_t.LOAD: + # TODO: change mem_addr to vsc type + if (pre_instr.category == riscv_instr_category_t.STORE and + pre_instr.mem_addr == self.mem_addr): + self.lsu_hazard = hazard_e["RAW_HAZARD"] + else: + self.lsu_hazard = hazard_e["NO_HAZARD"] + if self.category == riscv_instr_category_t.STORE: + if (pre_instr.category == riscv_instr_category_t.STORE and + pre_instr.mem_addr == self.mem_addr): + self.lsu_hazard = hazard_e["WAW_HAZARD"] + elif (pre_instr.category == riscv_instr_category_t.LOAD and + pre_instr.mem_addr == self.mem_addr): + self.lsu_hazard = hazard_e["WAR_HAZARD"] + else: + self.lsu_hazard = hazard_e["NO_HAZARD"] + logging.info("Pre: {}, Cur: {}, Hazard: {}/{}".format( + pre_instr.convert2asm(), self.convert2asm(), + self.gpr_hazard.name, self.lsu_hazard.name)) + + def update_src_regs(self, operands): + pass + + def update_dst_regs(self, reg_name, val_str): + pass + +class riscv_instr_cov_test(): + """ Main class for applying the functional coverage test """ + def __init__(self, argv): + self.trace = {} + self.csv_trace = argv + self.entry_cnt, self.total_entry_cnt, self.skipped_cnt, \ + self.unexpected_illegal_instr_cnt = 0, 0, 0, 0 + + def run_phase(self): + if not self.csv_trace: + sys.exit("No CSV file found!") + logging.info("{} CSV trace files to be " + "processed...\n".format(len(self.csv_trace))) + expect_illegal_instr = False + # Assuming we get list of csv files pathname from cov.py in argv + for csv_file in self.csv_trace: + with open("{}".format(csv_file)) as trace_file: + self.entry_cnt = 0 + header = [] + entry = [] + csv_reader = csv.reader(trace_file, delimiter=',') + line_count = 0 + # Get the header line + for row in csv_reader: + if line_count == 0: + header = row + logging.info("Header: {}".format(header)) + else: + entry = row + if len(entry) != len(header): + logging.info("Skipping malformed entry[{}]: " + "[{}]".format(self.entry_cnt, entry)) + self.skipped_cnt += 1 + else: + self.trace["csv_entry"] = row + for idx in range(len(header)): + if "illegal" in entry[idx]: + expect_illegal_instr = True + self.trace[header[idx]] = entry[idx] + if header[idx] != "pad": + logging.info("{} = {}".format(header[idx], + entry[idx])) + self.post_process_trace() + if self.trace["instr"] in ["li", "ret", "la"]: + pass + if "amo" in self.trace["instr"] or \ + "lr" in self.trace["instr"] or \ + "sc" in self.trace["instr"]: + # TODO: Enable functional coverage for AMO test + pass + if not self.sample(): + if not expect_illegal_instr: + logging.error("Found unexpected illegal " + "instr: {} " + "[{}]".format(self.trace[ + "instr"],entry)) + self.unexpected_illegal_instr_cnt += 1 + self.entry_cnt += 1 + line_count += 1 + logging.info("[{}]: {} instr processed".format(csv_file, + self.entry_cnt)) + self.total_entry_cnt += self.entry_cnt + logging.info("Finished processing {} trace CSV, {} " + "instructions".format(len(self.csv_trace), + self.total_entry_cnt)) + if self.skipped_cnt > 0 or self.unexpected_illegal_instr_cnt > 0: + logging.error("{} instruction skipped, {} illegal " + "instructions".format(self.skipped_cnt), + self.unexpected_illegal_instr_cnt) + + def post_process_trace(self): + pass + + def sample(self): + instr_name, binary = "", "" + binary = get_val(self.trace["binary"], hexa=1) + if binary[-2:] != "11": #TODO: and RV32C in supported_isa + #TODO: sample compressed instruction + pass + if binary[-2:] == "11": + #TODO: sampling + pass + #TODO: buch of if statements to check if the instruction name is valid + # and is a member of registered ones + instr_name = self.process_instr_name(self.trace["instr"]) + instruction = riscv_instr(instr_name) + #TODO: check the instruction group... + self.assign_trace_info_to_instr(instruction) + #TODO: instruction.pre_sample() and sample(instruction) + return True + + def assign_trace_info_to_instr(self, instruction): + operands, gpr_update, pair = [], [], [] + instruction.pc = get_val(self.trace["pc"], hexa=1) + instruction.binary = get_val(self.trace["binary"], hexa=1) + instruction.gpr = self.trace["gpr"] + instruction.csr = self.trace["csr"] + instruction.mode = self.trace["mode"] + instruction.trace = self.trace["instr_str"] + instruction.operands = self.trace["operand"] + operands = self.trace["operand"].split(",") + instruction.update_src_regs(operands) + gpr_update = self.trace["gpr"].split(";") + if len(gpr_update) == 1 and gpr_update[0] == "": + gpr_update = [] + for dest in gpr_update: + pair = dest.split(":") + if len(pair) != 2: + logging.error("Illegal gpr update format: {}".format(dest)) + instruction.update_dst_regs(pair[0], pair[1]) + instruction.pad = self.trace["pad"] + + def process_instr_name(self, instruction): + instruction = instruction.upper() + instruction.replace(".", "_") + instruction = self.update_instr_name(instruction) + return instruction + + def update_instr_name(self, instruction): + switcher = { + # Rename to new name as ovpsim still uses old name + "FMV_S_X": "FMV_W_X", + "FMV_X_S": "FMV_X_W", + # Convert pseudoinstructions + "FMV_S": "FSGNJ_S", + "FABS_S": "FSGNJX_S", + "FNEG_S": "FSGNJN_S", + "FMV_D": "FSGNJ_D", + "FABS_D": "FSGNJX_D", + "FNEG_D": "FSGNJN_D", + } + # if instruction is not present in the dictionary,second argument well + # be assigned as default value of passed argument + instruction = switcher.get(instruction, instruction) + return instruction + + +def main(argv): + cov_test = riscv_instr_cov_test(argv) + cov_test.run_phase() + +if __name__ == "__main__": + main(sys.argv) diff --git a/vendor/google_riscv-dv/src/isa/riscv_vector_instr.sv b/vendor/google_riscv-dv/src/isa/riscv_vector_instr.sv index 26f79e5f..5c23d865 100644 --- a/vendor/google_riscv-dv/src/isa/riscv_vector_instr.sv +++ b/vendor/google_riscv-dv/src/isa/riscv_vector_instr.sv @@ -183,7 +183,7 @@ class riscv_vector_instr extends riscv_floating_point_instr; // Vector register numbers accessed by the segment load or store would increment // cannot past 31 constraint nfields_c { - if (sub_extension == "zvlsseg") { + if (check_sub_extension(sub_extension, "zvlsseg")) { if (m_cfg.vector_cfg.vtype.vlmul < 8) { (nfields + 1) * m_cfg.vector_cfg.vtype.vlmul <= 8; if (category == LOAD) { @@ -261,13 +261,13 @@ class riscv_vector_instr extends riscv_floating_point_instr; } // 7.8.3 For vector indexed segment loads, the destination vector register groups // cannot overlap the source vectorregister group (specied by vs2), nor can they - // overlap the mask register if maske - if (format == VLX_FORMAT) { + // overlap the mask register if masked + // AMO instruction uses indexed address mode + if (format inside {VLX_FORMAT, VAMO_FORMAT}) { vd != vs2; } } - `uvm_object_utils(riscv_vector_instr) `uvm_object_new @@ -421,6 +421,15 @@ class riscv_vector_instr extends riscv_floating_point_instr; vs3.name(), rs1.name(), vs2.name()); end end + VAMO_FORMAT: begin + if (wd) begin + asm_str = $sformatf("%0s %0s,(%0s),%0s,%0s", get_instr_name(), vd.name(), + rs1.name(), vs2.name(), vd.name()); + end else begin + asm_str = $sformatf("%0s x0,(%0s),%0s,%0s", get_instr_name(), + rs1.name(), vs2.name(), vs3.name()); + end + end default: begin `uvm_fatal(`gfn, $sformatf("Unsupported format %0s", format.name())) end @@ -495,5 +504,9 @@ class riscv_vector_instr extends riscv_floating_point_instr; string suffix = instr_name.substr(prefix.len(), instr_name.len() - 1); return $sformatf("%0s%0d%0s", prefix, nfields + 1, suffix); endfunction - + + function bit check_sub_extension(string s, string literal); + return s == literal; + endfunction + endclass : riscv_vector_instr diff --git a/vendor/google_riscv-dv/src/isa/rv32v_instr.sv b/vendor/google_riscv-dv/src/isa/rv32v_instr.sv index 633a641c..cd5e6aef 100644 --- a/vendor/google_riscv-dv/src/isa/rv32v_instr.sv +++ b/vendor/google_riscv-dv/src/isa/rv32v_instr.sv @@ -298,3 +298,27 @@ `DEFINE_VA_INSTR(VSUXSEGH_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg") `DEFINE_VA_INSTR(VSUXSEGW_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg") `DEFINE_VA_INSTR(VSUXSEGE_V, VSX_FORMAT, STORE, RVV, {}, "zvlsseg") + +// ------------------------------------------------------------------------- +// Section 8. Vector AMO Operations (Zvamo) +// ------------------------------------------------------------------------- +// 32-bit vector AMOs +`DEFINE_VA_INSTR(VAMOSWAPW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOADDW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOXORW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOANDW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOORW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMINW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMAXW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMINUW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMAXUW_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +// SEW-bit vector AMOs +`DEFINE_VA_INSTR(VAMOSWAPE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOADDE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOXORE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOANDE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOORE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMINE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMAXE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMINUE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") +`DEFINE_VA_INSTR(VAMOMAXUE_V, VAMO_FORMAT, AMO, RVV, {}, "zvamo") diff --git a/vendor/google_riscv-dv/src/riscv_amo_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_amo_instr_lib.sv index 4258854e..77ff06f6 100644 --- a/vendor/google_riscv-dv/src/riscv_amo_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_amo_instr_lib.sv @@ -188,3 +188,32 @@ class riscv_amo_instr_stream extends riscv_amo_base_instr_stream; endfunction endclass : riscv_amo_instr_stream + + +class riscv_vector_amo_instr_stream extends riscv_vector_load_store_instr_stream; + + constraint amo_address_mode_c { + // AMO operation only supports word alignment or element alignemt + alignment inside {W_ALIGNMENT, E_ALIGNMENT}; + // AMO operation uses indexed address mode + address_mode == INDEXED; + // For the 32-bit vector AMO operations, SEW must be at least 32 bit + (cfg.vector_cfg.vtype.vsew < 32) -> (alignment != W_ALIGNMENT); + } + + `uvm_object_utils(riscv_vector_amo_instr_stream) + `uvm_object_new + + virtual function void add_element_vec_load_stores(); + allowed_instr = {VAMOSWAPE_V, VAMOADDE_V, VAMOXORE_V, + VAMOANDE_V, VAMOORE_V, VAMOMINE_V, + VAMOMAXE_V, VAMOMINUE_V, VAMOMAXUE_V, allowed_instr}; + endfunction + + virtual function void add_w_vec_load_stores(); + allowed_instr = {VAMOSWAPW_V, VAMOADDW_V, VAMOXORW_V, + VAMOANDW_V, VAMOORW_V, VAMOMINW_V, + VAMOMAXW_V, VAMOMINUW_V, VAMOMAXUW_V, allowed_instr}; + endfunction + +endclass : riscv_vector_amo_instr_stream diff --git a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv index 0202ff58..8a61e22e 100644 --- a/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv +++ b/vendor/google_riscv-dv/src/riscv_asm_program_gen.sv @@ -740,6 +740,8 @@ class riscv_asm_program_gen extends uvm_object; trap_vector_init(hart); // Setup PMP CSRs setup_pmp(hart); + // Generate PMPADDR write test sequence + gen_pmp_csr_write(hart); // Initialize PTE (link page table based on their real physical address) if(cfg.virtual_addr_translation_on) begin page_table_list.process_page_table(instr); @@ -831,6 +833,17 @@ class riscv_asm_program_gen extends uvm_object; end endfunction + // Generates a directed stream of instructions to write random values to all supported + // pmpaddr CSRs to test write accessibility. + // The original CSR values are restored afterwards. + virtual function void gen_pmp_csr_write(int hart); + string instr[$]; + if (riscv_instr_pkg::support_pmp && cfg.pmp_cfg.enable_write_pmp_csr) begin + cfg.pmp_cfg.gen_pmp_write_test({cfg.scratch_reg, cfg.pmp_reg}, instr); + gen_section(get_label("pmp_csr_write_test", hart), instr); + end + endfunction + // Handles creation of a subroutine to initialize any custom CSRs virtual function void setup_custom_csrs(int hart); string instr[$]; diff --git a/vendor/google_riscv-dv/src/riscv_illegal_instr.sv b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv index a1d1d2a1..a8dbbe64 100644 --- a/vendor/google_riscv-dv/src/riscv_illegal_instr.sv +++ b/vendor/google_riscv-dv/src/riscv_illegal_instr.sv @@ -166,11 +166,13 @@ class riscv_illegal_instr extends uvm_object; c_op != 2'b11; } - // Avoid generating illegal func3/func7 errors for opcode used by B-extension + // Avoid generating illegal func3/func7 errors for opcode used by B-extension for now + // + // TODO(udi): add support for generating illegal B-extension instructions constraint b_extension_c { if (RV32B inside {supported_isa}) { if (exception inside {kIllegalFunc3, kIllegalFunc7}) { - !(opcode inside {7'b0011011, 7'b0010011, 7'b0111011}); + !(opcode inside {7'b0110011, 7'b0010011, 7'b0111011}); } } } @@ -356,8 +358,8 @@ class riscv_illegal_instr extends uvm_object; if (riscv_instr_pkg::RV32A inside {riscv_instr_pkg::supported_isa}) begin legal_opcode = {legal_opcode, 7'b0101111}; end - if ((riscv_instr_pkg::RV64I inside {riscv_instr_pkg::supported_isa}) || - riscv_instr_pkg::RV64M inside {riscv_instr_pkg::supported_isa}) begin + if (riscv_instr_pkg::RV64I inside {riscv_instr_pkg::supported_isa} || + riscv_instr_pkg::RV64M inside {riscv_instr_pkg::supported_isa}) begin legal_opcode = {legal_opcode, 7'b0111011}; end if (riscv_instr_pkg::RV64I inside {riscv_instr_pkg::supported_isa}) begin diff --git a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv index 6e8268b9..5fc181ff 100644 --- a/vendor/google_riscv-dv/src/riscv_instr_pkg.sv +++ b/vendor/google_riscv-dv/src/riscv_instr_pkg.sv @@ -641,6 +641,7 @@ package riscv_instr_pkg; VLHUFF_V, VLWUFF_V, VLEFF_V, + // Segmented load/store instruction VLSEGE_V, VSSEGE_V, VLSEGB_V, @@ -685,6 +686,27 @@ package riscv_instr_pkg; VSUXSEGH_V, VSUXSEGW_V, VSUXSEGE_V, + // Vector AMO instruction + // 32-bit vector AMOs + VAMOSWAPW_V, + VAMOADDW_V, + VAMOXORW_V, + VAMOANDW_V, + VAMOORW_V, + VAMOMINW_V, + VAMOMAXW_V, + VAMOMINUW_V, + VAMOMAXUW_V, + // SEW-bit vector AMOs + VAMOSWAPE_V, + VAMOADDE_V, + VAMOXORE_V, + VAMOANDE_V, + VAMOORE_V, + VAMOMINE_V, + VAMOMAXE_V, + VAMOMINUE_V, + VAMOMAXUE_V, // Supervisor instruction DRET, MRET, @@ -747,7 +769,8 @@ package riscv_instr_pkg; VLX_FORMAT, VSX_FORMAT, VLS_FORMAT, - VSS_FORMAT + VSS_FORMAT, + VAMO_FORMAT } riscv_instr_format_t; diff --git a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv index 1495aa0a..e2e37331 100644 --- a/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv +++ b/vendor/google_riscv-dv/src/riscv_load_store_instr_lib.sv @@ -519,7 +519,7 @@ class riscv_load_store_rand_addr_instr_stream extends riscv_load_store_base_inst endclass -class riscv_vector_stride_load_store_instr_stream extends riscv_mem_access_stream; +class riscv_vector_load_store_instr_stream extends riscv_mem_access_stream; typedef enum {B_ALIGNMENT, H_ALIGNMENT, W_ALIGNMENT, E_ALIGNMENT} alignment_e; typedef enum {UNIT_STRIDED, STRIDED, INDEXED} address_mode_e; @@ -573,7 +573,7 @@ class riscv_vector_stride_load_store_instr_stream extends riscv_mem_access_strea int max_load_store_addr; riscv_vector_instr load_store_instr; - `uvm_object_utils(riscv_vector_stride_load_store_instr_stream) + `uvm_object_utils(riscv_vector_load_store_instr_stream) `uvm_object_new virtual function int get_addr_alignment_mask(int alignment_bytes); @@ -590,6 +590,7 @@ class riscv_vector_stride_load_store_instr_stream extends riscv_mem_access_strea if (address_mode == STRIDED) begin instr_list.push_front(get_init_gpr_instr(rs2_reg, stride_byte_offset)); end else if (address_mode == INDEXED) begin + // TODO: Support different index address for each element add_init_vector_gpr_instr(vs2_reg, index_addr); end super.post_randomize(); diff --git a/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv b/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv index 1bcadb83..7c2155b3 100644 --- a/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv +++ b/vendor/google_riscv-dv/src/riscv_pmp_cfg.sv @@ -39,6 +39,10 @@ class riscv_pmp_cfg extends uvm_object; // allowing all access restrictions to be enforced. bit enable_pmp_exception_handler = 1'b1; + // Setting this bit to 1'b1 enables generation of the directed stream of instructions to test + // write accesses to all supported pmpaddr[i] CSRs. + bit enable_write_pmp_csr; + // pmp CSR configurations rand pmp_cfg_reg_t pmp_cfg[]; @@ -129,6 +133,7 @@ class riscv_pmp_cfg extends uvm_object; get_int_arg_value("+pmp_granularity=", pmp_granularity); get_bool_arg_value("+pmp_randomize=", pmp_randomize); get_bool_arg_value("+pmp_allow_addr_overlap=", pmp_allow_addr_overlap); + get_bool_arg_value("+enable_write_pmp_csr=", enable_write_pmp_csr); get_hex_arg_value("+pmp_max_offset=", pmp_max_offset); `uvm_info(`gfn, $sformatf("pmp max offset: 0x%0x", pmp_max_offset), UVM_LOW) pmp_cfg = new[pmp_num_regions]; @@ -606,4 +611,61 @@ class riscv_pmp_cfg extends uvm_object; endfunction + // This function is used for a directed PMP test to test writes to all the pmpcfg and pmpaddr + // CSRs to test that writes succeed or fail appropriately. + virtual function void gen_pmp_write_test(riscv_reg_t scratch_reg[2], + ref string instr[$]); + + bit [11:0] pmp_addr; + bit [11:0] pmpcfg_addr; + bit [XLEN-1:0] pmp_val; + for (int i = 0; i < pmp_num_regions; i++) begin + pmp_addr = base_pmp_addr + i; + pmpcfg_addr = base_pmpcfg_addr + (i / cfg_per_csr); + // We randomize the upper 31 bits of pmp_val and then add this to the + // address of
, guaranteeing that the random value written to + // pmpaddr[i] doesn't interfere with the safe region. + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(pmp_val, pmp_val[31] == 1'b0;) + instr.push_back($sformatf("li x%0d, 0x%0x", scratch_reg[0], pmp_val)); + instr.push_back($sformatf("la x%0d, main", scratch_reg[1])); + instr.push_back($sformatf("add x%0d, x%0d, x%0d", + scratch_reg[0], scratch_reg[0], scratch_reg[1])); + // Write the randomized address to pmpaddr[i]. + // Original value of pmpaddr[i] will be written to scratch_reg[0]. + instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", + scratch_reg[0], pmp_addr, scratch_reg[0])); + // Restore the original address to pmpaddr[i]. + // New value of pmpaddr[i] will be written to scratch_reg[0]. + instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", + scratch_reg[0], pmp_addr, scratch_reg[0])); + // Randomize value to be written to pmpcfg CSR. + // + // TODO: support rv64. + `DV_CHECK_STD_RANDOMIZE_WITH_FATAL(pmp_val, + // Need to constrain pmp_val[7], pmp_val[15], ... to 1'b0 + // to ensure that the random config regions aren't locked + foreach (pmp_val[i]) { + if ((i+1) % 8 == 0) { + pmp_val[i] == 1'b0; + } + } + ) + // If we're writing to the pmpcfg CSR that contains region0 config information, + // ensure that the "safe" region remains fully accessible. + if (pmpcfg_addr == base_pmpcfg_addr) begin + pmp_val[7:0] = 'h0f; + end + instr.push_back($sformatf("li x%0d, 0x%0x", scratch_reg[0], pmp_val)); + // Write the randomized address to pmpcfg[i]. + // Original value of pmpcfg[i] will be written to scratch_reg[0]. + instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", + scratch_reg[0], pmpcfg_addr, scratch_reg[0])); + // Restore the original address to pmpcfg[i]. + // New value of pmpcfg[i] will be written to scratch_reg[0]. + instr.push_back($sformatf("csrrw x%0d, 0x%0x, x%0d", + scratch_reg[0], pmpcfg_addr, scratch_reg[0])); + end + + endfunction + endclass diff --git a/vendor/google_riscv-dv/src/riscv_privil_reg.sv b/vendor/google_riscv-dv/src/riscv_privil_reg.sv index b9cef94e..a4c519ad 100644 --- a/vendor/google_riscv-dv/src/riscv_privil_reg.sv +++ b/vendor/google_riscv-dv/src/riscv_privil_reg.sv @@ -282,15 +282,8 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t); // Physical Memory Protection Configuration Register PMPCFG1: begin privil_level = M_LEVEL; - if(XLEN==64) begin - add_field("PMP8CFG", 8, WARL); - add_field("PMP9CFG", 8, WARL); - add_field("PMP10CFG", 8, WARL); - add_field("PMP11CFG", 8, WARL); - add_field("PMP12CFG", 8, WARL); - add_field("PMP13CFG", 8, WARL); - add_field("PMP14CFG", 8, WARL); - add_field("PMP15CFG", 8, WARL); + if(XLEN!=32) begin + `uvm_fatal(`gfn, "CSR PMPCFG1 only exists in RV32.") end else begin add_field("PMP4CFG", 8, WARL); add_field("PMP5CFG", 8, WARL); @@ -300,14 +293,17 @@ class riscv_privil_reg extends riscv_reg#(privileged_reg_t); end // Physical Memory Protection Configuration Register PMPCFG2: begin - if(XLEN!=32) begin - `uvm_fatal(get_full_name(), "CSR PMPCFG2 only exists in RV32.") - end privil_level = M_LEVEL; add_field("PMP8CFG", 8, WARL); add_field("PMP9CFG", 8, WARL); add_field("PMP10CFG", 8, WARL); add_field("PMP11CFG", 8, WARL); + if(XLEN==64) begin + add_field("PMP12CFG", 8, WARL); + add_field("PMP13CFG", 8, WARL); + add_field("PMP14CFG", 8, WARL); + add_field("PMP15CFG", 8, WARL); + end end // Physical Memory Protection Configuration Register PMPCFG3: begin diff --git a/vendor/google_riscv-dv/target/rv64gcv/testlist.yaml b/vendor/google_riscv-dv/target/rv64gcv/testlist.yaml index 4f4fdea1..4bcd6079 100644 --- a/vendor/google_riscv-dv/target/rv64gcv/testlist.yaml +++ b/vendor/google_riscv-dv/target/rv64gcv/testlist.yaml @@ -74,8 +74,23 @@ +num_of_sub_program=0 +enable_floating_point=1 +enable_vector_extension=1 - +directed_instr_0=riscv_vector_stride_load_store_instr_stream,4 - +enable_fault_only_first_load=1 + +directed_instr_0=riscv_vector_load_store_instr_stream,10 + +no_branch_jump=1 + +boot_mode=m + +no_csr_instr=1 + iterations: 5 + gen_test: riscv_instr_base_test + rtl_test: core_base_test + +- test: riscv_vector_amo_test + description: > + Vector AMO random test + gen_opts: > + +instr_cnt=10000 + +num_of_sub_program=0 + +enable_floating_point=1 + +enable_vector_extension=1 + +directed_instr_0=riscv_vector_amo_instr_stream,10 +no_branch_jump=1 +boot_mode=m +no_csr_instr=1