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@17d7984
Vendor in some updates to PMP test generation. Update code from upstream repository https://github.com/google/riscv- dv to revision 17d79847e376a591cb3dcaae7601c98b0e70e8ac * Update pygen/pygen_src/isa/riscv_cov_instr.py (Hodjat Asghari Esfeden) * Minor issues fixed in the functional coverage flow (Hodjat Asghari Esfeden) * fix pmp offset constraint (Udi Jonnalagadda) * Fix minor issues (aneels3) * - Adds riscv_instr_cover_group file with a few covergroups - Confirms riscv_instr_cov_test script is up and running fine - Initializes the registers to 0 during their first gpr_state access (for ovpsim output log) (Hodjat Asghari Esfeden) * update directed pmp sequence constraint (Udi Jonnalagadda) * remove unreachable if...else statement (Udi Jonnalagadda) * update post_process() (aneels3) * add ecall_handler (aneels3) * Fix post_process() issue (aneels3) * Fix typo in post_process (aneels3) * Completed riscv_cov_instr class (decoupled from riscv_instr_cov_test file) Added private _riscv_cov_instr module to manually retrieve format/category/group/imm_t based on the name of the instruction (Hodjat Asghari Esfeden) * add post_process() (aneels3) Signed-off-by: Udi <udij@google.com>
This commit is contained in:
parent
d71aaeee06
commit
16ed993486
12 changed files with 2463 additions and 474 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: 61755c001bec0433fb69458f74d95476d2101cf3
|
||||
rev: 17d79847e376a591cb3dcaae7601c98b0e70e8ac
|
||||
}
|
||||
}
|
||||
|
|
517
vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_cov_instr.py
vendored
Normal file
517
vendor/google_riscv-dv/pygen/pygen_src/isa/riscv_cov_instr.py
vendored
Normal file
|
@ -0,0 +1,517 @@
|
|||
"""Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
import vsc
|
||||
import logging
|
||||
from enum import Enum, IntEnum, auto
|
||||
from bitstring import BitArray
|
||||
from pygen.pygen_src.target.rv32i import riscv_core_setting as rcs
|
||||
from pygen.pygen_src.riscv_instr_pkg import *
|
||||
|
||||
|
||||
class operand_sign_e(IntEnum):
|
||||
POSITIVE = 0
|
||||
NEGATIVE = auto()
|
||||
|
||||
|
||||
class div_result_e(IntEnum):
|
||||
DIV_NORMAL = 0
|
||||
DIV_BY_ZERO = auto()
|
||||
DIV_OVERFLOW = auto()
|
||||
|
||||
|
||||
class compare_result_e(IntEnum):
|
||||
EQUAL = 0
|
||||
LARGER = auto()
|
||||
SMALLER = auto()
|
||||
|
||||
|
||||
class logical_similarity_e(IntEnum):
|
||||
IDENTICAL = 0
|
||||
OPPOSITE = auto()
|
||||
SIMILAR = auto()
|
||||
DIFFERENT = auto()
|
||||
|
||||
|
||||
class special_val_e(IntEnum):
|
||||
NORMAL_VAL = 0
|
||||
MIN_VAL = auto()
|
||||
MAX_VAL = auto()
|
||||
ZERO_VAL = auto()
|
||||
|
||||
|
||||
class riscv_cov_instr:
|
||||
""" Class for a riscv instruction in functional coverage phase;
|
||||
data parsed from the CSV file fill different fields of an instruction """
|
||||
# class attr. to keep track of reg_name:reg_value throughout the program
|
||||
gpr_state = {}
|
||||
|
||||
def __init__(self):
|
||||
# Program counter (PC) of the instruction
|
||||
self.pc = vsc.bit_t(rcs.XLEN)
|
||||
rcs.XLEN) # Program counter (PC) of the instruction
|
||||
self.instr = None
|
||||
# self.gpr = None # destination operand of the instruction
|
||||
self.binary = vsc.bit_t(32) # 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 = vsc.int_t(rcs.XLEN)
|
||||
self.rs2_value = vsc.int_t(rcs.XLEN)
|
||||
self.rs3_value = vsc.int_t(rcs.XLEN)
|
||||
self.rd_value = vsc.int_t(rcs.XLEN)
|
||||
self.fs1_value = vsc.int_t(rcs.XLEN)
|
||||
self.fs2_value = vsc.int_t(rcs.XLEN)
|
||||
self.fs3_value = vsc.int_t(rcs.XLEN)
|
||||
self.fd_value = vsc.int_t(rcs.XLEN)
|
||||
|
||||
self.mem_addr = vsc.int_t(rcs.XLEN)
|
||||
self.unaligned_pc = 0
|
||||
self.unaligned_mem_access = 0
|
||||
self.compressed = 0
|
||||
self.branch_hit = 0
|
||||
self.div_result = None
|
||||
self.rs1_sign = 0
|
||||
self.rs2_sign = 0
|
||||
self.rs3_sign = 0
|
||||
self.fs1_sign = 0
|
||||
self.fs2_sign = 0
|
||||
self.fs3_sign = 0
|
||||
self.imm_sign = 0
|
||||
self.rd_sign = 0
|
||||
self.fd_sign = 0
|
||||
self.gpr_hazard = hazard_e.NO_HAZARD
|
||||
self.lsu_hazard = hazard_e.NO_HAZARD
|
||||
self.rs1_special_value = 0
|
||||
self.rs2_special_value = 0
|
||||
self.rs3_special_value = 0
|
||||
self.rd_special_value = 0
|
||||
self.imm_special_value = 0
|
||||
self.compare_result = 0
|
||||
self.logical_similarity = 0
|
||||
|
||||
self.group = None
|
||||
self.format = None
|
||||
self.category = None
|
||||
self.imm_type = None
|
||||
|
||||
self.csr = vsc.bit_t(12)
|
||||
''' TODO: rs2, rs1, rd, group, format, category, imm_type will be
|
||||
changed to vsc.enum_t once the issue with set/get_val is fixed '''
|
||||
self.rs2 = 0
|
||||
self.rs1 = 0
|
||||
self.rd = 0
|
||||
self.imm = vsc.int_t(32)
|
||||
self.has_rs1 = 1
|
||||
self.has_rs2 = 1
|
||||
self.has_rd = 1
|
||||
self.has_imm = 1
|
||||
self.imm_len = 0
|
||||
|
||||
def assign_attributes(self):
|
||||
attr_list = get_attr_list(self.instr)
|
||||
self.format = attr_list[0]
|
||||
self.category = attr_list[1]
|
||||
self.group = attr_list[2]
|
||||
self.imm_type = imm_t.IMM
|
||||
if len(attr_list) > 3:
|
||||
self.imm_type = attr_list[3]
|
||||
self.set_imm_len()
|
||||
self.set_mode()
|
||||
|
||||
def set_imm_len(self):
|
||||
if self.format.name in ["U_FORMAT", "J_FORMAT"]:
|
||||
self.imm_len = 20
|
||||
elif self.format.name in ["I_FORMAT", "S_FORMAT", "B_FORMAT"]:
|
||||
if self.imm_type.name == "UIMM":
|
||||
self.imm_len = 5
|
||||
else:
|
||||
self.imm_len = 11
|
||||
|
||||
def set_mode(self):
|
||||
# mode setting for Instruction Format
|
||||
if self.format.name == "R_FORMAT":
|
||||
self.has_imm = 0
|
||||
if self.format.name == "I_FORMAT":
|
||||
self.has_rs2 = 0
|
||||
if self.format.name in ["S_FORMAT", "B_FORMAT"]:
|
||||
self.has_rd = 0
|
||||
if self.format.name in ["U_FORMAT", "J_FORMAT"]:
|
||||
self.has_rs1 = 0
|
||||
self.has_rs2 = 0
|
||||
|
||||
# mode setting for Instruction Category
|
||||
if self.category.name == "CSR":
|
||||
self.has_rs2 = 0
|
||||
if self.format.name == "I_FORMAT":
|
||||
self.has_rs1 = 0
|
||||
|
||||
def pre_sample(self):
|
||||
unaligned_pc = self.pc.get_val() % 4 != 0
|
||||
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.name not in ["R_FORMAT", "CR_FORMAT"]:
|
||||
self.imm_special_value = self.get_imm_special_val(self.imm)
|
||||
if self.category.name in ["COMPARE", "BRANCH"]:
|
||||
self.compare_result = self.get_compare_result()
|
||||
if self.category.name in ["LOAD", "STORE"]:
|
||||
self.mem_addr.set_val(self.rs1_value.get_val() +
|
||||
self.imm.get_val())
|
||||
self.unaligned_mem_access = self.is_unaligned_mem_access()
|
||||
if self.unaligned_mem_access:
|
||||
logging.info("Unaligned: {}, mem_addr: {}".format(
|
||||
self.instr.name, self.mem_addr.get_val()))
|
||||
if self.category.name == "LOGICAL":
|
||||
self.logical_similarity = self.get_logical_similarity()
|
||||
if self.category.name == "BRANCH":
|
||||
self.branch_hit = self.is_branch_hit()
|
||||
if self.instr.name in ["DIV", "DIVU", "REM", "REMU", "DIVW", "DIVUW",
|
||||
"REMW", "REMUW"]:
|
||||
self.div_result = self.get_div_result()
|
||||
|
||||
@staticmethod
|
||||
def get_operand_sign(operand):
|
||||
# TODO: Currently handled using string formatting as part select
|
||||
# isn't yet supported for global vsc variables
|
||||
operand_bin = format(operand.get_val(), '#0{}b'.format(rcs.XLEN + 2))
|
||||
# "0b" is the prefix, so operand_bin[2] is the sign bit
|
||||
if operand_bin[2] == "0":
|
||||
return operand_sign_e["POSITIVE"]
|
||||
else:
|
||||
return operand_sign_e["NEGATIVE"]
|
||||
|
||||
def is_unaligned_mem_access(self):
|
||||
if (self.instr.name in ["LWU", "LD", "SD", "C_LD", "C_SD"] and
|
||||
self.mem_addr.get_val() % 8 != 0):
|
||||
return 1
|
||||
elif (self.instr.name in ["LW", "SW", "C_LW", "C_SW"] and
|
||||
self.mem_addr.get_val() % 4 != 0):
|
||||
return 1
|
||||
elif (self.instr.name in ["LH", "LHU", "SH"] and
|
||||
self.mem_addr.get_val() % 2 != 0):
|
||||
return 1
|
||||
return 0
|
||||
|
||||
@staticmethod
|
||||
def get_imm_sign(imm):
|
||||
# TODO: Currently handled using string formatting as part select
|
||||
# isn't yet supported for global vsc variables
|
||||
imm_bin = format(imm.get_val(), '#0{}b'.format(rcs.XLEN + 2))
|
||||
# "0b" is the prefix, so imm_bin[2] is the sign bit
|
||||
if imm_bin[2] == "0":
|
||||
return operand_sign_e["POSITIVE"]
|
||||
else:
|
||||
return operand_sign_e["NEGATIVE"]
|
||||
|
||||
def get_div_result(self):
|
||||
if self.rs2_value.get_val() == 0:
|
||||
return div_result_e["DIV_BY_ZERO"]
|
||||
elif (self.rs2_value.get_val() == 1
|
||||
and self.rs1_value.get_val() == (1 << (rcs.XLEN - 1))):
|
||||
return div_result_e["DIV_OVERFLOW"]
|
||||
else:
|
||||
return div_result_e["DIV_NORMAL"]
|
||||
|
||||
@staticmethod
|
||||
def get_operand_special_value(operand):
|
||||
if operand.get_val() == 0:
|
||||
return special_val_e["ZERO_VAL"]
|
||||
elif operand.get_val() == 1 << (rcs.XLEN - 1):
|
||||
return special_val_e["MIN_VAL"]
|
||||
elif operand.get_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.get_val() == 0:
|
||||
return special_val_e["ZERO_VAL"]
|
||||
elif self.format == riscv_instr_format_t.U_FORMAT:
|
||||
# unsigned immediate value
|
||||
max_val = vsc.int_t(32, (1 << self.imm_len) - 1)
|
||||
if imm.get_val() == 0:
|
||||
return special_val_e["MIN_VAL"]
|
||||
if imm.get_val() == max_val.get_val():
|
||||
return special_val_e["MAX_VAL"]
|
||||
else:
|
||||
# signed immediate value
|
||||
max_val = vsc.int_t(32, (2 ** (self.imm_len - 1)) - 1)
|
||||
min_val = vsc.int_t(32, -2 ** (self.imm_len - 1))
|
||||
if min_val.get_val() == imm.get_val():
|
||||
return special_val_e["MIN_VAL"]
|
||||
if max_val.get_val() == imm.get_val():
|
||||
return special_val_e["MAX_VAL"]
|
||||
return special_val_e["NORMAL_VAL"]
|
||||
|
||||
def get_compare_result(self):
|
||||
val1 = vsc.int_t(rcs.XLEN, self.rs1_value.get_val())
|
||||
val2 = vsc.int_t(rcs.XLEN, self.imm.get_val() if (
|
||||
self.format == riscv_instr_format_t.I_FORMAT) else
|
||||
self.rs2_value.val)
|
||||
if val1.get_val() == val2.get_val():
|
||||
return compare_result_e["EQUAL"]
|
||||
elif val1.get_val() < val2.get_val():
|
||||
return compare_result_e["SMALLER"]
|
||||
else:
|
||||
return compare_result_e["LARGER"]
|
||||
|
||||
def is_branch_hit(self):
|
||||
if self.instr.name == "BEQ":
|
||||
return int(self.rs1_value.get_val() == self.rs2_value.get_val())
|
||||
elif self.instr.name == "C_BEQZ":
|
||||
return int(self.rs1_value.get_val() == 0)
|
||||
elif self.instr.name == "BNE":
|
||||
return int(self.rs1_value.get_val() != self.rs2_value.get_val())
|
||||
elif self.instr.name == "C_BNEZ":
|
||||
return int(self.rs1_value.get_val() != 0)
|
||||
elif self.instr.name == "BLT" or self.instr.name == "BLTU":
|
||||
return int(self.rs1_value.get_val() < self.rs2_value.get_val())
|
||||
elif self.instr.name == "BGE" or self.instr.name == "BGEU":
|
||||
return int(self.rs1_value.get_val() >= self.rs2_value.get_val())
|
||||
else:
|
||||
logging.error("Unexpected instruction {}".format(self.instr.name))
|
||||
|
||||
def get_logical_similarity(self):
|
||||
val1 = vsc.int_t(rcs.XLEN, self.rs1_value.get_val())
|
||||
val2 = vsc.int_t(rcs.XLEN, (self.imm.get_val() if
|
||||
self.format == riscv_instr_format_t.I_FORMAT
|
||||
else self.rs2_value.val))
|
||||
temp = bin(val1.get_val() ^ val2.get_val())
|
||||
bit_difference = len([[ones for ones in temp[2:] if ones == '1']])
|
||||
if val1.get_val() == val2.get_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: There are cases where instruction actually has destination but
|
||||
ovpsim doesn't log it because of no change in its value. Hence,
|
||||
the result of the check_hazard_condition won't be accurate. Need to
|
||||
explicitly extract the destination register from the operands '''
|
||||
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:
|
||||
if (pre_instr.category == riscv_instr_category_t.STORE and
|
||||
pre_instr.mem_addr.get_val() == self.mem_addr.get_val()):
|
||||
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.get_val() == self.mem_addr.get_val()):
|
||||
self.lsu_hazard = hazard_e["WAW_HAZARD"]
|
||||
elif (pre_instr.category == riscv_instr_category_t.LOAD and
|
||||
pre_instr.mem_addr.get_val() == self.mem_addr.get_val()):
|
||||
self.lsu_hazard = hazard_e["WAR_HAZARD"]
|
||||
else:
|
||||
self.lsu_hazard = hazard_e["NO_HAZARD"]
|
||||
logging.debug("Pre PC/name: {}/{}, Cur PC/name: {}/{}, "
|
||||
"Hazard: {}/{}".format(pre_instr.pc.get_val(),
|
||||
pre_instr.instr.name,
|
||||
self.pc.get_val(),
|
||||
self.instr.name,
|
||||
self.gpr_hazard.name,
|
||||
self.lsu_hazard.name))
|
||||
|
||||
def get_instr_name(self):
|
||||
get_instr_name = self.instr.name
|
||||
for i in get_instr_name:
|
||||
if i == "_":
|
||||
get_instr_name = get_instr_name.replace(i, ".")
|
||||
return get_instr_name
|
||||
|
||||
def update_src_regs(self, operands):
|
||||
if self.format.name in ["J_FORMAT", "U_FORMAT"]:
|
||||
# instr rd,imm
|
||||
assert len(operands) == 2
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.format.name == "I_FORMAT":
|
||||
assert len(operands) == 3
|
||||
if self.category.name == "LOAD":
|
||||
# load rd, imm(rs1)
|
||||
self.rs1 = self.get_gpr(operands[2])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[2]))
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.category.name == "CSR":
|
||||
# csrrwi rd, csr, imm
|
||||
self.imm.set_val(get_val(operands[2]))
|
||||
if operands[1].upper() in privileged_reg_t.__members__:
|
||||
self.csr.set_val(
|
||||
privileged_reg_t[operands[1].upper()].value)
|
||||
else:
|
||||
self.csr.set_val(get_val(operands[1]))
|
||||
else:
|
||||
# addi rd, rs1, imm
|
||||
self.rs1 = self.get_gpr(operands[1])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[1]))
|
||||
self.imm.set_val(get_val(operands[2]))
|
||||
elif self.format.name in ["S_FORMAT", "B_FORMAT"]:
|
||||
assert len(operands) == 3
|
||||
if self.category.name == "STORE":
|
||||
self.rs2 = self.get_gpr(operands[0])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[0]))
|
||||
self.rs1 = self.get_gpr(operands[2])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[2]))
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
else:
|
||||
# bne rs1, rs2, imm
|
||||
self.rs1 = self.get_gpr(operands[0])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[0]))
|
||||
self.rs2 = self.get_gpr(operands[1])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[1]))
|
||||
self.imm.set_val(get_val(operands[2]))
|
||||
elif self.format.name == "R_FORMAT":
|
||||
if self.has_rs2 or self.category.name == "CSR":
|
||||
assert len(operands) == 3
|
||||
else:
|
||||
assert len(operands) == 2
|
||||
if self.category.name == "CSR":
|
||||
# csrrw rd, csr, rs1
|
||||
if operands[1].upper() in privileged_reg_t.__members__:
|
||||
self.csr.set_val(
|
||||
privileged_reg_t[operands[1].upper()].value)
|
||||
else:
|
||||
self.csr.set_val(get_val(operands[1]))
|
||||
self.rs1 = self.get_gpr(operands[2])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[2]))
|
||||
else:
|
||||
# add rd, rs1, rs2
|
||||
self.rs1 = self.get_gpr(operands[1])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[1]))
|
||||
if self.has_rs2:
|
||||
self.rs2 = self.get_gpr(operands[2])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[2]))
|
||||
elif self.format.name == "R4_FORMAT":
|
||||
assert len(operands) == 4
|
||||
self.rs1 = self.get_gpr(operands[1])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[1]))
|
||||
self.rs2 = self.get_gpr(operands[2])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[2]))
|
||||
self.rs2 = self.get_gpr(operands[3])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[3]))
|
||||
elif self.format.name in ["CI_FORMAT", "CIW_FORMAT"]:
|
||||
if self.instr.name == "C_ADDI16SP":
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
self.rs1 = riscv_reg_t.SP
|
||||
self.rs1_value.set_val(self.get_gpr_state("sp"))
|
||||
elif self.instr.name == "C_ADDI4SPN":
|
||||
self.rs1 = riscv_reg_t.SP
|
||||
self.rs1_value.set_val(self.get_gpr_state("sp"))
|
||||
elif self.instr.name in ["C_LDSP", "C_LWSP", "C_LQSP"]:
|
||||
# c.ldsp rd, imm
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
self.rs1 = riscv_reg_t.SP
|
||||
self.rs1_value.set_val(self.get_gpr_state("sp"))
|
||||
else:
|
||||
# c.lui rd, imm
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.format.name == "CL_FORMAT":
|
||||
# c.lw rd, imm(rs1)
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
self.rs1 = self.get_gpr(operands[2])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[2]))
|
||||
elif self.format.name == "CS_FORMAT":
|
||||
# c.sw rs2,imm(rs1)
|
||||
self.rs2 = self.get_gpr(operands[0])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[0]))
|
||||
self.rs1 = self.get_gpr(operands[2])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[2]))
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.format.name == "CA_FORMAT":
|
||||
# c.and rd, rs2 (rs1 == rd)
|
||||
self.rs2 = self.get_gpr(operands[1])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[1]))
|
||||
self.rs1 = self.get_gpr(operands[0])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[0]))
|
||||
elif self.format.name == "CB_FORMAT":
|
||||
# c.beqz rs1, imm
|
||||
self.rs1 = self.get_gpr(operands[0])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[0]))
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.format.name == "CSS_FORMAT":
|
||||
# c.swsp rs2, imm
|
||||
self.rs2 = self.get_gpr(operands[0])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[0]))
|
||||
self.rs1 = riscv_reg_t.SP
|
||||
self.rs1_value.set_val(self.get_gpr_state("sp"))
|
||||
self.imm.set_val(get_val(operands[1]))
|
||||
elif self.format.name == "CR_FORMAT":
|
||||
if self.instr.name in ["C_JR", "C_JALR"]:
|
||||
# c.jalr rs1
|
||||
self.rs1 = self.get_gpr(operands[0])
|
||||
self.rs1_value.set_val(self.get_gpr_state(operands[0]))
|
||||
else:
|
||||
# c.add rd, rs2
|
||||
self.rs2 = self.get_gpr(operands[1])
|
||||
self.rs2_value.set_val(self.get_gpr_state(operands[1]))
|
||||
elif self.format.name == "CJ_FORMAT":
|
||||
# c.j imm
|
||||
self.imm.set_val(get_val(operands[0]))
|
||||
else:
|
||||
logging.error("Unsupported format {}".format(self.format.name))
|
||||
|
||||
def update_dst_regs(self, reg_name, val_str):
|
||||
riscv_cov_instr.gpr_state[reg_name] = get_val(val_str, hexa=1)
|
||||
self.rd = self.get_gpr(reg_name)
|
||||
self.rd_value.set_val(self.get_gpr_state(reg_name))
|
||||
|
||||
@staticmethod
|
||||
def get_gpr(reg_name):
|
||||
reg_name = reg_name.upper()
|
||||
if reg_name not in riscv_reg_t.__members__:
|
||||
logging.error("Cannot convert {} to GPR".format(reg_name))
|
||||
return riscv_reg_t[reg_name]
|
||||
|
||||
@staticmethod
|
||||
def get_gpr_state(name):
|
||||
if name in ["zero", "x0"]:
|
||||
return 0
|
||||
elif name in riscv_cov_instr.gpr_state:
|
||||
return riscv_cov_instr.gpr_state[name]
|
||||
else:
|
||||
logging.warning(
|
||||
"Cannot find GPR state: {}; initialize to 0".format(name))
|
||||
if name.upper() in riscv_reg_t.__members__:
|
||||
riscv_cov_instr.gpr_state[name] = 0
|
||||
return 0
|
|
@ -1,46 +0,0 @@
|
|||
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))
|
|
@ -84,6 +84,7 @@ class riscv_asm_program_gen:
|
|||
self.main_program[hart].label_name = "main"
|
||||
self.main_program[hart].gen_instr(is_main_program = 1, no_branch = cfg.no_branch_jump)
|
||||
|
||||
self.main_program[hart].post_process_instr()
|
||||
self.main_program[hart].generate_instr_stream()
|
||||
logging.info("Generating main program instruction stream...done")
|
||||
self.instr_stream.extend(self.main_program[hart].instr_string_list)
|
||||
|
@ -292,7 +293,20 @@ class riscv_asm_program_gen:
|
|||
self.instr_stream.append(pkg_ins.indent + "ecall")
|
||||
|
||||
def gen_register_dump(self):
|
||||
pass
|
||||
string = ""
|
||||
# load base address
|
||||
string = "{}la x{}, _start".format(pkg_ins.indent, cfg.gpr[0].value)
|
||||
self.instr_stream.append(string)
|
||||
|
||||
# Generate sw/sd instructions
|
||||
for i in range(32):
|
||||
if (rcs.XLEN == 64):
|
||||
string = "{}sd x{}, {}(x{})".format(
|
||||
pkg_ins.indent, i, i * (rcs.XLEN / 8), cfg.gpr[0].value)
|
||||
else:
|
||||
string = "{}sw x{}, {}(x{})".format(
|
||||
pkg_ins.indent, i, int(i * (rcs.XLEN / 8)), cfg.gpr[0].value)
|
||||
self.instr_stream.append(string)
|
||||
|
||||
def pre_enter_privileged_mode(self, hart):
|
||||
instr = []
|
||||
|
@ -430,7 +444,15 @@ class riscv_asm_program_gen:
|
|||
pass
|
||||
|
||||
def gen_ecall_handler(self, hart):
|
||||
pass
|
||||
string = ""
|
||||
string = pkg_ins.format_string(pkg_ins.get_label(
|
||||
"ecall_handler:", hart), pkg_ins.LABEL_STR_LEN)
|
||||
self.instr_stream.append(string)
|
||||
self.dump_perf_stats()
|
||||
self.gen_register_dump()
|
||||
string = pkg_ins.format_string(" ", pkg_ins.LABEL_STR_LEN)
|
||||
string = string + "j write_tohost"
|
||||
self.instr_stream.append(string)
|
||||
|
||||
def gen_ebreak_handler(self, hart):
|
||||
pass
|
||||
|
|
1353
vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_cover_group.py
vendored
Normal file
1353
vendor/google_riscv-dv/pygen/pygen_src/riscv_instr_cover_group.py
vendored
Normal file
File diff suppressed because it is too large
Load diff
|
@ -317,9 +317,8 @@ def parse_args():
|
|||
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 = 1)
|
||||
choices = [0, 1], type = int, default = 0)
|
||||
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',
|
||||
|
|
|
@ -13,9 +13,9 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|||
"""
|
||||
|
||||
import logging
|
||||
from enum import Enum, auto
|
||||
from enum import Enum, IntEnum, auto
|
||||
from bitstring import BitArray
|
||||
from pygen_src.target.rv32i import riscv_core_setting as rcs
|
||||
from pygen.pygen_src.target.rv32i import riscv_core_setting as rcs
|
||||
|
||||
|
||||
class mem_region_t:
|
||||
|
@ -560,7 +560,7 @@ class riscv_instr_name_t(Enum):
|
|||
INVALID_INSTR = auto()
|
||||
|
||||
|
||||
class riscv_reg_t(Enum):
|
||||
class riscv_reg_t(IntEnum):
|
||||
ZERO = 0
|
||||
RA = auto()
|
||||
SP = auto()
|
||||
|
@ -722,6 +722,8 @@ class riscv_instr_category_t(Enum):
|
|||
TRAP = auto()
|
||||
INTERRUPT = auto()
|
||||
AMO = auto()
|
||||
|
||||
|
||||
# typedef bit[11:0] riscv_csr_t;
|
||||
|
||||
|
||||
|
@ -1055,13 +1057,68 @@ class misa_ext_t(Enum):
|
|||
MISA_EXT_Z = auto()
|
||||
|
||||
|
||||
class hazard_e(Enum):
|
||||
class hazard_e(IntEnum):
|
||||
NO_HAZARD = 0
|
||||
RAW_HAZARD = auto()
|
||||
WAR_HAZARD = auto()
|
||||
WAW_HAZARD = auto()
|
||||
|
||||
|
||||
# TODO: ignore bins is not yet supported in pyvsc; extra enums will be removed
|
||||
# once support is added
|
||||
# Ignore WAR/WAW_HAZARD for branch instructions
|
||||
class branch_hazard_e(IntEnum):
|
||||
NO_HAZARD = 0
|
||||
RAW_HAZARD = auto()
|
||||
|
||||
# Ignore RAW_HAZARD for store lsu hazard
|
||||
class store_lsu_hazard_e(IntEnum):
|
||||
NO_HAZARD = 0
|
||||
WAR_HAZARD = auto()
|
||||
WAW_HAZARD = auto()
|
||||
|
||||
|
||||
# RA/T1 for rs1/rd_link in jalr instruction
|
||||
class jalr_riscv_reg_t(IntEnum):
|
||||
RA = 0
|
||||
T1 = auto()
|
||||
|
||||
|
||||
# Ignore ZERO as src1 of load instructions
|
||||
class riscv_reg_ex_zero_t(IntEnum):
|
||||
RA = 0
|
||||
SP = auto()
|
||||
GP = auto()
|
||||
TP = auto()
|
||||
T0 = auto()
|
||||
T1 = auto()
|
||||
T2 = auto()
|
||||
S0 = auto()
|
||||
S1 = auto()
|
||||
A0 = auto()
|
||||
A1 = auto()
|
||||
A2 = auto()
|
||||
A3 = auto()
|
||||
A4 = auto()
|
||||
A5 = auto()
|
||||
A6 = auto()
|
||||
A7 = auto()
|
||||
S2 = auto()
|
||||
S3 = auto()
|
||||
S4 = auto()
|
||||
S5 = auto()
|
||||
S6 = auto()
|
||||
S7 = auto()
|
||||
S8 = auto()
|
||||
S9 = auto()
|
||||
S10 = auto()
|
||||
S11 = auto()
|
||||
T3 = auto()
|
||||
T4 = auto()
|
||||
T5 = auto()
|
||||
T6 = auto()
|
||||
|
||||
|
||||
class pmp_addr_mode_t(Enum):
|
||||
OFF = 0b00
|
||||
TOR = 0b01
|
||||
|
@ -1161,29 +1218,215 @@ 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))
|
||||
out_val = 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
|
||||
if hexa:
|
||||
out_val = int(in_string, base=16)
|
||||
else:
|
||||
out_val = int(in_string)
|
||||
logging.info("imm: {} -> {}".format(in_string, out_val))
|
||||
return out_val
|
||||
|
||||
|
||||
def get_attr_list(instr_name):
|
||||
switcher = {
|
||||
# LOAD instructions
|
||||
riscv_instr_name_t.LB: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOAD,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.LH: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOAD,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.LW: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOAD,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.LBU: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOAD,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.LHU: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOAD,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# STORE instructions
|
||||
riscv_instr_name_t.SB: [riscv_instr_format_t.S_FORMAT,
|
||||
riscv_instr_category_t.STORE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SH: [riscv_instr_format_t.S_FORMAT,
|
||||
riscv_instr_category_t.STORE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SW: [riscv_instr_format_t.S_FORMAT,
|
||||
riscv_instr_category_t.STORE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# SHIFT intructions
|
||||
riscv_instr_name_t.SLL: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SLLI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SRL: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SRLI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SRA: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SRAI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SHIFT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# ARITHMETIC intructions
|
||||
riscv_instr_name_t.ADD: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.ADDI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.NOP: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SUB: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.LUI: [riscv_instr_format_t.U_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.AUIPC: [riscv_instr_format_t.U_FORMAT,
|
||||
riscv_instr_category_t.ARITHMETIC,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
# LOGICAL instructions
|
||||
riscv_instr_name_t.XOR: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.XORI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.OR: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.ORI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.AND: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.ANDI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.LOGICAL,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# COMPARE instructions
|
||||
riscv_instr_name_t.SLT: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.COMPARE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SLTI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.COMPARE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SLTU: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.COMPARE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SLTIU: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.COMPARE,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# BRANCH instructions
|
||||
riscv_instr_name_t.BEQ: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.BNE: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.BLT: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.BGE: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.BLTU: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.BGEU: [riscv_instr_format_t.B_FORMAT,
|
||||
riscv_instr_category_t.BRANCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# JUMP instructions
|
||||
riscv_instr_name_t.JAL: [riscv_instr_format_t.J_FORMAT,
|
||||
riscv_instr_category_t.JUMP,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.JALR: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.JUMP,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# SYNCH instructions
|
||||
riscv_instr_name_t.FENCE: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYNCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.FENCE_I: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYNCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SFENCE_VMA: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.SYNCH,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# SYSTEM instructions
|
||||
riscv_instr_name_t.ECALL: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.EBREAK: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.URET: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.SRET: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.MRET: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.DRET: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.SYSTEM,
|
||||
riscv_instr_group_t.RV32I],
|
||||
riscv_instr_name_t.WFI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.INTERRUPT,
|
||||
riscv_instr_group_t.RV32I],
|
||||
# CSR instructions
|
||||
riscv_instr_name_t.CSRRW: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.CSRRS: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.CSRRC: [riscv_instr_format_t.R_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.CSRRWI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.CSRRSI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
riscv_instr_name_t.CSRRCI: [riscv_instr_format_t.I_FORMAT,
|
||||
riscv_instr_category_t.CSR,
|
||||
riscv_instr_group_t.RV32I, imm_t.UIMM],
|
||||
}
|
||||
# if instruction is not present in the dictionary,second argument well
|
||||
# be assigned as default value of passed argument
|
||||
attr_list = switcher.get(instr_name, "Cannot find instruction")
|
||||
return attr_list
|
||||
|
||||
|
||||
def add_functions_as_methods(function):
|
||||
def decorator(Class):
|
||||
setattr(Class, function.__name__, function)
|
||||
return Class
|
||||
|
||||
return decorator
|
||||
|
||||
class hazard_e(Enum):
|
||||
NO_HAZARD = 0
|
||||
RAW_HAZARD = auto()
|
||||
WAR_HAZARD = auto()
|
||||
WAW_HAZARD = auto()
|
||||
|
||||
class riscv_instr_pkg:
|
||||
def __init__(self):
|
||||
self.MPRV_BIT_MASK = BitArray(uint= 0x1 << 0x17, length = rcs.XLEN)
|
||||
self.SUM_BIT_MASK = BitArray(uint = 0x1 << 0x18, length = rcs.XLEN)
|
||||
self.MPP_BIT_MASK = BitArray(uint = 0x3 << 0x11, length = rcs.XLEN)
|
||||
self.MPRV_BIT_MASK = BitArray(uint=0x1 << 0x17, length=rcs.XLEN)
|
||||
self.SUM_BIT_MASK = BitArray(uint=0x1 << 0x18, length=rcs.XLEN)
|
||||
self.MPP_BIT_MASK = BitArray(uint=0x3 << 0x11, length=rcs.XLEN)
|
||||
self.MAX_USED_VADDR_BITS = 30
|
||||
self.IMM25_WIDTH = 25
|
||||
self.IMM12_WIDTH = 12
|
||||
|
@ -1196,8 +1439,8 @@ class riscv_instr_pkg:
|
|||
self.MAX_CALL_PER_FUNC = 5
|
||||
self.indent = self.LABEL_STR_LEN * " "
|
||||
|
||||
def hart_prefix(self, hart = 0):
|
||||
if(rcs.NUM_HARTS <= 1):
|
||||
def hart_prefix(self, hart=0):
|
||||
if (rcs.NUM_HARTS <= 1):
|
||||
return ""
|
||||
else:
|
||||
return f"h{hart}_"
|
||||
|
@ -1205,17 +1448,18 @@ class riscv_instr_pkg:
|
|||
def get_label(self, label, hart=0):
|
||||
return (self.hart_prefix(hart) + label)
|
||||
|
||||
def format_string(self, string, length = 10):
|
||||
def format_string(self, string, length=10):
|
||||
formatted_str = length * " "
|
||||
if (int(length) < len(string)):
|
||||
return string
|
||||
formatted_str = string + formatted_str[0: (int(length) - len(string))]
|
||||
return formatted_str
|
||||
|
||||
def format_data(self, data, byte_per_group = 4):
|
||||
def format_data(self, data, byte_per_group=4):
|
||||
string = "0x"
|
||||
for i in range(len(data)):
|
||||
if ((i % byte_per_group == 0) and (i != len(data) - 1) and (i != 0)):
|
||||
if ((i % byte_per_group == 0) and (i != len(data) - 1) and (
|
||||
i != 0)):
|
||||
string = string + ", 0x"
|
||||
string = string + f"{hex(data[i])}"
|
||||
return string
|
||||
|
|
|
@ -1,89 +1,188 @@
|
|||
"""
|
||||
Copyright 2020 Google LLC
|
||||
Copyright 2020 PerfectVIPs Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
"""
|
||||
import logging
|
||||
from pygen_src.riscv_instr_stream import riscv_rand_instr_stream
|
||||
from pygen_src.riscv_instr_pkg import pkg_ins
|
||||
|
||||
|
||||
class riscv_instr_sequence:
|
||||
|
||||
def __init__(self):
|
||||
self.instr_cnt = 0
|
||||
self.instr_stream = riscv_rand_instr_stream()
|
||||
self.is_main_program = 0
|
||||
self.is_debug_program = 0
|
||||
self.label_name = ""
|
||||
self.instr_string_list = [] # Save the instruction list
|
||||
self.program_stack_len = 0 # Stack space allocated for this program
|
||||
self.directed_instr = [] # List of all directed instruction stream
|
||||
self.illegal_instr_pct = 0 # Percentage of illegal instructions
|
||||
self.hint_instr_pct = 0 # Percentage of hint instructions
|
||||
|
||||
def gen_instr(self, is_main_program, no_branch = 1):
|
||||
self.is_main_program = is_main_program
|
||||
self.instr_stream.initialize_instr_list(self.instr_cnt)
|
||||
logging.info("Start generating %d instruction" % len(self.instr_stream.instr_list))
|
||||
self.instr_stream.gen_instr(no_branch = no_branch, no_load_store = 1,
|
||||
is_debug_program = self.is_debug_program)
|
||||
|
||||
if not is_main_program:
|
||||
self.gen_stack_enter_instr()
|
||||
self.gen_stack_exit_instr()
|
||||
|
||||
# TODO
|
||||
def gen_stack_enter_instr(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def gen_stack_exit_instr(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def post_process_instr(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def insert_jump_instr(self):
|
||||
pass
|
||||
|
||||
def generate_instr_stream(self, no_label = 0):
|
||||
prefix = ''
|
||||
string = ''
|
||||
self.instr_string_list.clear()
|
||||
|
||||
for i in range(len(self.instr_stream.instr_list)):
|
||||
if i == 0:
|
||||
if no_label:
|
||||
prefix = pkg_ins.format_string(string = ' ', length = pkg_ins.LABEL_STR_LEN)
|
||||
else:
|
||||
prefix = pkg_ins.format_string(string = '{}:'.format(
|
||||
self.label_name), length = pkg_ins.LABEL_STR_LEN)
|
||||
|
||||
self.instr_stream.instr_list[i].has_label = 1
|
||||
else:
|
||||
if(self.instr_stream.instr_list[i].has_label):
|
||||
prefix = pkg_ins.format_string(string = '{}'.format(
|
||||
self.instr_stream.instr_list[i].label), length = pkg_ins.LABEL_STR_LEN)
|
||||
else:
|
||||
prefix = pkg_ins.format_string(string = " ", length = pkg_ins.LABEL_STR_LEN)
|
||||
string = prefix + self.instr_stream.instr_list[i].convert2asm()
|
||||
self.instr_string_list.append(string)
|
||||
|
||||
# TODO
|
||||
def generate_return_routine(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def insert_illegal_hint_instr(self):
|
||||
pass
|
||||
"""
|
||||
Copyright 2020 Google LLC
|
||||
Copyright 2020 PerfectVIPs Inc.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
||||
"""
|
||||
import logging
|
||||
import random
|
||||
from collections import defaultdict
|
||||
from pygen_src.riscv_instr_stream import riscv_rand_instr_stream
|
||||
from pygen_src.riscv_instr_gen_config import cfg
|
||||
from pygen_src.riscv_instr_pkg import pkg_ins
|
||||
|
||||
|
||||
class riscv_instr_sequence:
|
||||
|
||||
def __init__(self):
|
||||
self.instr_cnt = 0
|
||||
self.instr_stream = riscv_rand_instr_stream()
|
||||
self.is_main_program = 0
|
||||
self.is_debug_program = 0
|
||||
self.label_name = ""
|
||||
self.instr_string_list = [] # Save the instruction list
|
||||
self.program_stack_len = 0 # Stack space allocated for this program
|
||||
self.directed_instr = [] # List of all directed instruction stream
|
||||
self.illegal_instr_pct = 0 # Percentage of illegal instructions
|
||||
self.hint_instr_pct = 0 # Percentage of hint instructions
|
||||
self.branch_idx = [None] * 30
|
||||
|
||||
def gen_instr(self, is_main_program, no_branch = 1):
|
||||
self.is_main_program = is_main_program
|
||||
self.instr_stream.initialize_instr_list(self.instr_cnt)
|
||||
logging.info("Start generating %d instruction" % len(self.instr_stream.instr_list))
|
||||
self.instr_stream.gen_instr(no_branch = no_branch, no_load_store = 1,
|
||||
is_debug_program = self.is_debug_program)
|
||||
|
||||
if not is_main_program:
|
||||
self.gen_stack_enter_instr()
|
||||
self.gen_stack_exit_instr()
|
||||
|
||||
# TODO
|
||||
def gen_stack_enter_instr(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def gen_stack_exit_instr(self):
|
||||
pass
|
||||
|
||||
'''
|
||||
----------------------------------------------------------------------------------------------
|
||||
Instruction post-process
|
||||
|
||||
Post-process is required for branch instructions:
|
||||
|
||||
Need to assign a valid branch target. This is done by picking a random instruction label in
|
||||
this sequence and assigning to the branch instruction. All the non-atomic instructions
|
||||
will have a unique numeric label as the local branch target identifier.
|
||||
The atomic instruction streams don't have labels except for the first instruction. This is
|
||||
to avoid branching into an atomic instruction stream which breaks its atomicy. The
|
||||
definition of an atomic instruction stream here is a sequence of instructions which must be
|
||||
executed in-order.
|
||||
In this sequence, only forward branch is handled. The backward branch target is implemented
|
||||
in a dedicated loop instruction sequence. Randomly choosing a backward branch target could
|
||||
lead to dead loops in the absence of proper loop exiting conditions.
|
||||
----------------------------------------------------------------------------------------------
|
||||
'''
|
||||
|
||||
def post_process_instr(self):
|
||||
label_idx = 0
|
||||
branch_cnt = 0
|
||||
j = 0
|
||||
branch_target = defaultdict(lambda: None)
|
||||
|
||||
for instr in self.directed_instr:
|
||||
self.instr_stream.insert_instr_stream(instr.instr_list)
|
||||
'''
|
||||
Assign an index for all instructions, these indexes wont change
|
||||
even a new instruction is injected in the post process.
|
||||
'''
|
||||
for i in range(len(self.instr_stream.instr_list)):
|
||||
self.instr_stream.instr_list[i].idx = label_idx
|
||||
if(self.instr_stream.instr_list[i].has_label and
|
||||
not(self.instr_stream.instr_list[i].atomic)):
|
||||
if((self.illegal_instr_pct > 0) and
|
||||
(self.instr_stream.instr_list[i].insert_illegal_instr == 0)):
|
||||
'''
|
||||
The illegal instruction generator always increase PC by 4 when resume execution,
|
||||
need to make sure PC + 4 is at the correct instruction boundary.
|
||||
'''
|
||||
if(self.instr_stream.instr_list[i].is_compressed):
|
||||
if(i < (len(self.instr_stream.instr_list) - 1)):
|
||||
if(self.instr_stream.instr_list[i + 1].is_compressed):
|
||||
self.instr_stream.instr_list[i].is_illegal_instr = random.randrange(
|
||||
0, min(100, self.illegal_instr_pct))
|
||||
else:
|
||||
self.instr_stream.instr_list[i].is_illegal_instr = random.randrange(
|
||||
0, min(100, self.illegal_instr_pct))
|
||||
if(self.hint_instr_pct > 0 and
|
||||
(self.instr_stream.instr_list[i].is_illegal_instr == 0)):
|
||||
if(self.instr_stream.instr_list[i].is_compressed):
|
||||
self.instr_stream.instr_list[i].is_hint_instr = random.randrange(
|
||||
0, min(100, self.hint_instr_pct))
|
||||
|
||||
self.instr_stream.instr_list[i].label = "{}".format(label_idx)
|
||||
self.instr_stream.instr_list[i].is_local_numeric_label = 1
|
||||
label_idx += 1
|
||||
|
||||
# Generate branch target
|
||||
for i in range(len(self.branch_idx)):
|
||||
self.branch_idx[i] = random.randint(1, cfg.max_branch_step)
|
||||
|
||||
while(j < len(self.instr_stream.instr_list)):
|
||||
if((self.instr_stream.instr_list[j].category.name == "BRANCH") and
|
||||
(not self.instr_stream.instr_list[j].branch_assigned) and
|
||||
(not self.instr_stream.instr_list[j].is_illegal_instr)):
|
||||
'''
|
||||
Post process the branch instructions to give a valid local label
|
||||
Here we only allow forward branch to avoid unexpected infinite loop
|
||||
The loop structure will be inserted with a separate routine using
|
||||
reserved loop registers
|
||||
'''
|
||||
branch_target_label = 0
|
||||
branch_byte_offset = 0
|
||||
branch_target_label = self.instr_stream.instr_list[j].idx + \
|
||||
self.branch_idx[branch_cnt]
|
||||
if(branch_target_label >= label_idx):
|
||||
branch_target_label = label_idx - 1
|
||||
branch_cnt += 1
|
||||
if(branch_cnt == len(self.branch_idx)):
|
||||
branch_cnt = 0
|
||||
random.shuffle(self.branch_idx)
|
||||
logging.info("Processing branch instruction[%0d]:%0s # %0d -> %0d", j,
|
||||
self.instr_stream.instr_list[j].convert2asm(),
|
||||
self.instr_stream.instr_list[j].idx, branch_target_label)
|
||||
self.instr_stream.instr_list[j].imm_str = "{}f".format(branch_target_label)
|
||||
self.instr_stream.instr_list[j].branch_assigned = 1
|
||||
branch_target[branch_target_label] = 1
|
||||
|
||||
# Remove the local label which is not used as branch target
|
||||
if(self.instr_stream.instr_list[j].has_label and
|
||||
self.instr_stream.instr_list[j].is_local_numeric_label):
|
||||
idx = int(self.instr_stream.instr_list[j].label)
|
||||
if(not branch_target[idx]):
|
||||
self.instr_stream.instr_list[j].has_label = 0
|
||||
j += 1
|
||||
logging.info("Finished post-processing instructions")
|
||||
|
||||
def insert_jump_instr(self):
|
||||
pass # TODO
|
||||
|
||||
def generate_instr_stream(self, no_label = 0):
|
||||
prefix = ''
|
||||
string = ''
|
||||
self.instr_string_list.clear()
|
||||
|
||||
for i in range(len(self.instr_stream.instr_list)):
|
||||
if i == 0:
|
||||
if no_label:
|
||||
prefix = pkg_ins.format_string(string = ' ', length = pkg_ins.LABEL_STR_LEN)
|
||||
else:
|
||||
prefix = pkg_ins.format_string(string = '{}:'.format(
|
||||
self.label_name), length = pkg_ins.LABEL_STR_LEN)
|
||||
|
||||
self.instr_stream.instr_list[i].has_label = 1
|
||||
else:
|
||||
if(self.instr_stream.instr_list[i].has_label):
|
||||
prefix = pkg_ins.format_string(string = '{}:'.format(
|
||||
self.instr_stream.instr_list[i].label), length = pkg_ins.LABEL_STR_LEN)
|
||||
else:
|
||||
prefix = pkg_ins.format_string(string = " ", length = pkg_ins.LABEL_STR_LEN)
|
||||
string = prefix + self.instr_stream.instr_list[i].convert2asm()
|
||||
self.instr_string_list.append(string)
|
||||
prefix = pkg_ins.format_string(str(i), pkg_ins.LABEL_STR_LEN)
|
||||
|
||||
# TODO
|
||||
def generate_return_routine(self):
|
||||
pass
|
||||
|
||||
# TODO
|
||||
def insert_illegal_hint_instr(self):
|
||||
pass
|
||||
|
|
|
@ -1,270 +1,36 @@
|
|||
# Lint as: python3
|
||||
"""Tests for riscv_instr_cov."""
|
||||
"""Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
"""
|
||||
|
||||
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 *
|
||||
import vsc
|
||||
import csv
|
||||
from tabulate import *
|
||||
from pygen.pygen_src.isa.riscv_cov_instr import riscv_cov_instr
|
||||
from pygen.pygen_src.riscv_instr_cover_group 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_two_files_new.log', filemode='w',
|
||||
format="%(filename)s %(lineno)s %(levelname)s %(message)s",
|
||||
level=logging.ERROR)
|
||||
|
||||
|
||||
|
||||
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():
|
||||
class riscv_instr_cov_test:
|
||||
""" Main class for applying the functional coverage test """
|
||||
|
||||
def __init__(self, argv):
|
||||
self.instr_cg = riscv_instr_cover_group()
|
||||
self.trace = {}
|
||||
self.csv_trace = argv
|
||||
self.entry_cnt, self.total_entry_cnt, self.skipped_cnt, \
|
||||
|
@ -281,7 +47,7 @@ class riscv_instr_cov_test():
|
|||
with open("{}".format(csv_file)) as trace_file:
|
||||
self.entry_cnt = 0
|
||||
header = []
|
||||
entry = []
|
||||
self.instr_cg.reset()
|
||||
csv_reader = csv.reader(trace_file, delimiter=',')
|
||||
line_count = 0
|
||||
# Get the header line
|
||||
|
@ -297,27 +63,30 @@ class riscv_instr_cov_test():
|
|||
self.skipped_cnt += 1
|
||||
else:
|
||||
self.trace["csv_entry"] = row
|
||||
logging.info("-----------------------------"
|
||||
"-----------------------------")
|
||||
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]))
|
||||
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"]:
|
||||
continue
|
||||
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
|
||||
continue
|
||||
if not self.sample():
|
||||
if not expect_illegal_instr:
|
||||
logging.error("Found unexpected illegal "
|
||||
"instr: {} "
|
||||
"[{}]".format(self.trace[
|
||||
"instr"],entry))
|
||||
"instr"],
|
||||
entry))
|
||||
self.unexpected_illegal_instr_cnt += 1
|
||||
self.entry_cnt += 1
|
||||
line_count += 1
|
||||
|
@ -326,53 +95,82 @@ class riscv_instr_cov_test():
|
|||
self.total_entry_cnt += self.entry_cnt
|
||||
logging.info("Finished processing {} trace CSV, {} "
|
||||
"instructions".format(len(self.csv_trace),
|
||||
self.total_entry_cnt))
|
||||
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)
|
||||
"instructions".format(self.skipped_cnt,
|
||||
self.unexpected_illegal_instr_cnt))
|
||||
self.get_coverage_report()
|
||||
|
||||
@staticmethod
|
||||
def get_coverage_report():
|
||||
model = vsc.get_coverage_report_model()
|
||||
file = open('CoverageReport.txt', 'w')
|
||||
file.write("Groups Coverage Summary\n")
|
||||
file.write("Total groups in report: {}\n".format(
|
||||
len(model.covergroups)))
|
||||
headers = ["SCORE", "WEIGHT", "NAME"]
|
||||
table = []
|
||||
for cg in model.covergroups:
|
||||
table.append([cg.coverage, cg.weight, cg.name])
|
||||
file.write(tabulate(table, headers, tablefmt="grid",
|
||||
numalign="center", stralign="center"))
|
||||
file.close()
|
||||
|
||||
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
|
||||
binary = vsc.int_t(rcs.XLEN)
|
||||
binary.set_val(get_val(self.trace["binary"], hexa=1))
|
||||
# TODO: Currently handled using string formatting as part select
|
||||
# isn't yet supported for global vsc variables
|
||||
# width is rcs.XLEN+2 because of 0b in the beginning of binary_bin
|
||||
binary_bin = format(binary.get_val(), '#0{}b'.format(rcs.XLEN + 2))
|
||||
if binary_bin[-2:] != "11": # TODO: and RV32C in supported_isa
|
||||
# TODO: sample compressed instruction
|
||||
pass
|
||||
if binary[-2:] == "11":
|
||||
#TODO: sampling
|
||||
if binary_bin[-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
|
||||
processed_instr_name = self.process_instr_name(self.trace["instr"])
|
||||
if processed_instr_name in riscv_instr_name_t.__members__:
|
||||
instr_name = riscv_instr_name_t[processed_instr_name]
|
||||
instruction = riscv_cov_instr()
|
||||
instruction.instr = instr_name
|
||||
# cov_instr is created, time to manually assign attributes
|
||||
# TODO: This will get fixed later when we get an inst from template
|
||||
instruction.assign_attributes()
|
||||
if instruction.group.name in ["RV32I", "RV32M", "RV32C", "RV64I",
|
||||
"RV64M", "RV64C", "RV32F", "RV64F",
|
||||
"RV32D", "RV64D", "RV32B", "RV64B"]:
|
||||
self.assign_trace_info_to_instr(instruction)
|
||||
instruction.pre_sample()
|
||||
self.instr_cg.sample(instruction)
|
||||
return True
|
||||
logging.info("Cannot find opcode: {}".format(processed_instr_name))
|
||||
return False
|
||||
|
||||
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.pc.set_val(get_val(self.trace["pc"], hexa=1))
|
||||
instruction.binary.set_val(get_val(self.trace["binary"], hexa=1))
|
||||
instruction.trace = self.trace["instr_str"]
|
||||
instruction.operands = self.trace["operand"]
|
||||
if instruction.instr.name in ["NOP", "WFI", "FENCE", "FENCE_I",
|
||||
"EBREAK", "C_EBREAK", "SFENCE_VMA",
|
||||
"ECALL", "C_NOP", "MRET", "SRET",
|
||||
"URET"]:
|
||||
return
|
||||
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] == "":
|
||||
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()
|
||||
|
@ -380,18 +178,19 @@ class riscv_instr_cov_test():
|
|||
instruction = self.update_instr_name(instruction)
|
||||
return instruction
|
||||
|
||||
def update_instr_name(self, instruction):
|
||||
@staticmethod
|
||||
def update_instr_name(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",
|
||||
"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
|
||||
|
@ -399,9 +198,10 @@ class riscv_instr_cov_test():
|
|||
return instruction
|
||||
|
||||
|
||||
def main(argv):
|
||||
def main(argv):
|
||||
cov_test = riscv_instr_cov_test(argv)
|
||||
cov_test.run_phase()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main(sys.argv)
|
||||
|
|
1
vendor/google_riscv-dv/requirements.txt
vendored
1
vendor/google_riscv-dv/requirements.txt
vendored
|
@ -8,3 +8,4 @@ sphinx_rtd_theme
|
|||
rst2pdf
|
||||
flake8
|
||||
pyvsc
|
||||
tabulate
|
||||
|
|
|
@ -160,9 +160,6 @@
|
|||
// unsigend immediate value
|
||||
bit [31:0] max_val;
|
||||
max_val = (1 << imm_len)-1;
|
||||
if (value == '0) begin
|
||||
return MIN_VAL;
|
||||
end
|
||||
if (value == max_val) begin
|
||||
return MAX_VAL;
|
||||
end
|
||||
|
|
11
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
11
vendor/google_riscv-dv/src/riscv_pmp_cfg.sv
vendored
|
@ -97,7 +97,7 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
// Offset of pmp_cfg[0] does not matter, since it will be set to <main>,
|
||||
// so we do not constrain it here, as it will be overridden during generation
|
||||
if (i != 0) {
|
||||
pmp_cfg[i].offset inside {[1 : pmp_max_offset + 1]};
|
||||
pmp_cfg[i].offset inside {[1 : pmp_max_offset]};
|
||||
} else {
|
||||
pmp_cfg[i].offset == 0;
|
||||
}
|
||||
|
@ -622,7 +622,7 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
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
|
||||
// We randomize the lower 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;)
|
||||
|
@ -642,12 +642,15 @@ class riscv_pmp_cfg extends uvm_object;
|
|||
//
|
||||
// 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]) {
|
||||
// constrain each Lock bit to 0
|
||||
if ((i+1) % 8 == 0) {
|
||||
pmp_val[i] == 1'b0;
|
||||
}
|
||||
// prevent W=1/R=0 combination
|
||||
if (i % 8 == 0) { // this is an R bit
|
||||
!((pmp_val[i] == 0) && (pmp_val[i+1] == 1'b1));
|
||||
}
|
||||
}
|
||||
)
|
||||
// If we're writing to the pmpcfg CSR that contains region0 config information,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue