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:
Udi 2020-08-14 17:31:18 -07:00 committed by udinator
parent d71aaeee06
commit 16ed993486
12 changed files with 2463 additions and 474 deletions

View file

@ -9,6 +9,6 @@
upstream:
{
url: https://github.com/google/riscv-dv
rev: 61755c001bec0433fb69458f74d95476d2101cf3
rev: 17d79847e376a591cb3dcaae7601c98b0e70e8ac
}
}

View 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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

View file

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

View file

@ -8,3 +8,4 @@ sphinx_rtd_theme
rst2pdf
flake8
pyvsc
tabulate

View file

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

View file

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