mirror of
https://github.com/lowRISC/ibex.git
synced 2025-04-22 04:47:25 -04:00
Update google_riscv-dv to google/riscv-dv@61755c0
Update code from upstream repository https://github.com/google/riscv- dv to revision 61755c001bec0433fb69458f74d95476d2101cf3 * Adds new PMP directed sequence. (Udi Jonnalagadda) * Fix typo (aneels3) * Add gpr_c constraint (aneels3) * Corrections of a code formatting. (Dariusz Stachanczyk) * Modify asm, config and pkg files. (aneels3) * fix riscv_privil_reg compile error (google/riscv-dv#666) (udinator) * Added methods to the coverage test file (Hodjat Asghari Esfeden) * Constraints should contain only intergral types - fix added for a string variable used in nfields_c constraint. (Dariusz Stachanczyk) * Minor fixes on coverage test (Hodjat Asghari Esfeden) * fix pmpcfg csr definitions (Udi Jonnalagadda) * Pygen: minor fix (danghai) * Pre_sampling extension (Hodjat Asghari Esfeden) * Fix opcode in b_extension_c constraint (google/riscv-dv#659) (udinator) * Add vector AMO instruction support (google/riscv-dv#658) (taoliug) * Terminate when it cannot insert instruction (danghai) * Riscv_instr_cov added, riscv_instr_cov_test extended, comment applied (except for csv_dir) (Hodjat Asghari Esfeden) * Fix Indentation (aneels3) * fix imm constraint issue (aneels3) * fix typo in extend_imm() (aneels3) * Hodjat (Hodjat Asghari Esfeden) Signed-off-by: Udi <udij@google.com>
This commit is contained in:
parent
7eaf0e4a6e
commit
3ddc92a0fa
20 changed files with 907 additions and 49 deletions
2
vendor/google_riscv-dv.lock.hjson
vendored
2
vendor/google_riscv-dv.lock.hjson
vendored
|
@ -9,6 +9,6 @@
|
|||
upstream:
|
||||
{
|
||||
url: https://github.com/google/riscv-dv
|
||||
rev: 3cf691dcb96f2cd72250690216b60f2b0c0ac804
|
||||
rev: 61755c001bec0433fb69458f74d95476d2101cf3
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"):
|
||||
|
|
46
vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr_cov.py
vendored
Normal file
46
vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_instr_cov.py
vendored
Normal file
|
@ -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))
|
|
@ -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
|
||||
|
|
|
@ -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',
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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']
|
||||
|
||||
|
|
407
vendor/google_riscv-dv/pygen/pygen_src/test/riscv_instr_cov_test.py
vendored
Normal file
407
vendor/google_riscv-dv/pygen/pygen_src/test/riscv_instr_cov_test.py
vendored
Normal file
|
@ -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)
|
|
@ -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
|
||||
|
|
24
vendor/google_riscv-dv/src/isa/rv32v_instr.sv
vendored
24
vendor/google_riscv-dv/src/isa/rv32v_instr.sv
vendored
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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[$];
|
||||
|
|
|
@ -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
|
||||
|
|
25
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
25
vendor/google_riscv-dv/src/riscv_instr_pkg.sv
vendored
|
@ -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;
|
||||
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
62
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
62
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
|
@ -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 <main>, 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
|
||||
|
|
20
vendor/google_riscv-dv/src/riscv_privil_reg.sv
vendored
20
vendor/google_riscv-dv/src/riscv_privil_reg.sv
vendored
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue