forked from XS-MLVP/UnityChipForXiangShan
397 lines
17 KiB
Python
397 lines
17 KiB
Python
import toffee
|
|
import os
|
|
import pytest
|
|
import ctypes
|
|
import datetime
|
|
import toffee.funcov as fc
|
|
|
|
from dut.predecode.UT_PreDecode import *
|
|
from dut.decodestage.UT_DecodeStage import *
|
|
|
|
from comm import get_out_dir, get_root_dir, debug, UT_FCOV, get_file_logger
|
|
from dut.rvcexpander.UT_RVCExpander import *
|
|
|
|
from toffee_test.reporter import set_func_coverage
|
|
from toffee_test.reporter import set_line_coverage
|
|
|
|
g = fc.CovGroup(UT_FCOV("INT", parent=-2))
|
|
|
|
def init_function_coverage(g):
|
|
# Add decoder module test point content: ToDO
|
|
pass
|
|
|
|
toffee.setup_logging(toffee.ERROR)
|
|
|
|
|
|
class PreDecodeWrapper(toffee.Bundle):
|
|
def __init__(self, dut: DUTPreDecode):
|
|
super().__init__()
|
|
self.dut = dut
|
|
self.input_instrution = toffee.Bundle.from_prefix("io_in_bits_", dut)
|
|
self.output_instrution = toffee.Bundle.from_prefix("io_out_", dut)
|
|
self.bind(dut)
|
|
|
|
def predecode(self, instr, need_step=1):
|
|
if not isinstance(instr, list):
|
|
instr = [instr]
|
|
predecoded_inst = []
|
|
for i in range(0, len(instr), 16):
|
|
sub_list = instr[i:i+16]
|
|
for j, inst in enumerate(sub_list):
|
|
self.input_instrution[f"data_%d"%j].value = inst
|
|
self.dut.Step(need_step)
|
|
predecoded_inst.extend([self.output_instrution["instr_%d"%k].value for k in range(16)])
|
|
return predecoded_inst
|
|
|
|
|
|
class DecodeWrapper(toffee.Bundle):
|
|
def __init__(self, dut: DUTDecodeStage):
|
|
super().__init__()
|
|
self.dut = dut
|
|
for i in range(6):
|
|
setattr(self, f"in_data_{i}", toffee.Bundle.from_prefix(f"io_in_{i}_", dut))
|
|
setattr(self, f"out_data_{i}", toffee.Bundle.from_prefix(f"io_out_{i}_", dut))
|
|
self.input_inst = [getattr(self, f"in_data_{i}") for i in range(6)]
|
|
self.output_instrution = [getattr(self, f"out_data_{i}") for i in range(6)]
|
|
self.bind(dut)
|
|
|
|
def SetDefaultValue(self):
|
|
########## set default sinput ignal value ###############
|
|
self.dut.io_redirect.value = 0b0
|
|
|
|
######### set instruction releated signals
|
|
for i in range(6):
|
|
self.input_inst[i].valid.value = 0
|
|
self.input_inst[i].bits_instr.value = 0
|
|
self.input_inst[i].bits_foldpc.value = 0
|
|
self.input_inst[i].bits_isFetchMalAddr = 0
|
|
self.input_inst[i].bits_trigger.value = 0
|
|
self.input_inst[i].bits_preDecodeInfo_isRVC.value = 0
|
|
self.input_inst[i].bits_preDecodeInfo_brType.value = 0
|
|
self.input_inst[i].bits_pred_taken.value = 0
|
|
self.input_inst[i].bits_crossPageIPFFix.value = 0
|
|
self.input_inst[i].bits_ftqPtr_flag.value = 0
|
|
self.input_inst[i].bits_ftqPtr_value.value = 0
|
|
self.input_inst[i].bits_ftqOffset.value = 0
|
|
|
|
for j in range(24):
|
|
setattr(self.dut, f'io_in_{i}_bits_exceptionVec_{j}', 0)
|
|
|
|
########## set req ready signals from rename stage #######
|
|
self.dut.io_out_0_ready.value = 0b1
|
|
self.dut.io_out_1_ready.value = 0b1
|
|
self.dut.io_out_2_ready.value = 0b1
|
|
self.dut.io_out_3_ready.value = 0b1
|
|
self.dut.io_out_4_ready.value = 0b1
|
|
self.dut.io_out_5_ready.value = 0b1
|
|
|
|
########## singlestep signal temporarily set to 0 (Because it may be hard to compare with ref model)
|
|
self.dut.io_csrCtrl_singlestep.value = 0b0
|
|
|
|
########## fromCSR #######################################
|
|
self.dut.io_fromCSR_illegalInst_sfenceVMA.value = 0
|
|
self.dut.io_fromCSR_illegalInst_sfencePart.value = 0
|
|
self.dut.io_fromCSR_illegalInst_hfenceGVMA.value = 0
|
|
self.dut.io_fromCSR_illegalInst_hfenceVVMA.value = 0
|
|
self.dut.io_fromCSR_illegalInst_hlsv.value = 0
|
|
self.dut.io_fromCSR_illegalInst_fsIsOff.value = 0
|
|
self.dut.io_fromCSR_illegalInst_vsIsOff.value = 0
|
|
self.dut.io_fromCSR_illegalInst_wfi.value = 0
|
|
self.dut.io_fromCSR_illegalInst_frm.value = 0
|
|
self.dut.io_fromCSR_virtualInst_sfenceVMA.value = 0
|
|
self.dut.io_fromCSR_virtualInst_sfencePart.value = 0
|
|
self.dut.io_fromCSR_virtualInst_hfence.value = 0
|
|
self.dut.io_fromCSR_virtualInst_hlsv.value = 0
|
|
self.dut.io_fromCSR_virtualInst_wfi.value = 0
|
|
|
|
######### fusion no use ##################################
|
|
self.dut.io_fusion_0.value = 0b0
|
|
self.dut.io_fusion_1.value = 0b0
|
|
self.dut.io_fusion_2.value = 0b0
|
|
########## VType signals temporarily set to 0 (Because it may be hard to compare with ref model)
|
|
self.dut.io_fromRob_isResumeVType.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_valid.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_bits_illegal.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_bits_vma.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_bits_vta.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_bits_vsew.value = 0b0
|
|
self.dut.io_fromRob_commitVType_vtype_bits_vlmul.value = 0b0
|
|
self.dut.io_fromRob_commitVType_hasVsetvl.value = 0b0
|
|
|
|
self.dut.io_fromRob_walkVType_valid.value = 0b0
|
|
self.dut.io_fromRob_walkVType_bits_illegal.value = 0b0
|
|
self.dut.io_fromRob_walkVType_bits_vma.value = 0b0
|
|
self.dut.io_fromRob_walkVType_bits_vta.value = 0b0
|
|
self.dut.io_fromRob_walkVType_bits_vsew.value = 0
|
|
self.dut.io_fromRob_walkVType_bits_vlmul.value = 0
|
|
self.dut.io_vsetvlVType_illegal.value = 0b0
|
|
self.dut.io_vsetvlVType_vma.value = 0b0
|
|
self.dut.io_vsetvlVType_vta.value = 0b0
|
|
self.dut.io_vsetvlVType_vsew.value = 0
|
|
self.dut.io_vsetvlVType_vlmul.value = 0
|
|
self.dut.io_vstart.value = 0
|
|
|
|
def Reset(self):
|
|
self.dut.reset.value = 0
|
|
self.dut.Step(1)
|
|
self.dut.reset.value = 1
|
|
self.dut.Step(2)
|
|
self.dut.reset.value = 0
|
|
self.dut.Step(1)
|
|
|
|
def Input_instruction(self, i, valid, instr, isRVC, brType, isCall, isRet, pred_taken, instr_ex):
|
|
self.input_inst[i].valid.value = valid
|
|
self.input_inst[i].bits_instr.value = instr
|
|
self.input_inst[i].bits_foldpc.value = 0
|
|
self.input_inst[i].bits_exceptionVec_2.value = instr_ex
|
|
|
|
for j in range(24):
|
|
setattr(self.dut, f'io_in_{i}_bits_exceptionVec_{j}', 0)
|
|
|
|
self.input_inst[i].bits_trigger.value = 0
|
|
self.input_inst[i].bits_preDecodeInfo_isRVC.value = isRVC
|
|
self.input_inst[i].bits_preDecodeInfo_brType.value = brType
|
|
self.input_inst[i].bits_pred_taken.value = pred_taken
|
|
self.input_inst[i].bits_crossPageIPFFix.value = 0
|
|
self.input_inst[i].bits_ftqPtr_flag.value = 0
|
|
self.input_inst[i].bits_ftqPtr_value.value = 0
|
|
self.input_inst[i].bits_ftqOffset.value = 0
|
|
|
|
def FromCSR_illegalInst(self, sfenceVMA, sfencePart, hfenceGVMA, hfenceVVMA, hlsv, fsIsOff, vsIsOff, wfi, frm):
|
|
self.dut.io_fromCSR_illegalInst_sfenceVMA.value = sfenceVMA
|
|
self.dut.io_fromCSR_illegalInst_sfencePart.value = sfencePart
|
|
self.dut.io_fromCSR_illegalInst_hfenceGVMA.value = hfenceGVMA
|
|
self.dut.io_fromCSR_illegalInst_hfenceVVMA.value = hfenceVVMA
|
|
self.dut.io_fromCSR_illegalInst_hlsv.value = hlsv
|
|
self.dut.io_fromCSR_illegalInst_fsIsOff.value = fsIsOff
|
|
self.dut.io_fromCSR_illegalInst_vsIsOff.value = vsIsOff
|
|
self.dut.io_fromCSR_illegalInst_wfi.value = wfi
|
|
self.dut.io_fromCSR_illegalInst_frm.value = frm
|
|
|
|
|
|
def FromCSR_virtualInst(self, sfenceVMA, sfencePart, hfence, hlsv, wfi):
|
|
self.dut.io_fromCSR_virtualInst_sfenceVMA.value = sfenceVMA
|
|
self.dut.io_fromCSR_virtualInst_sfencePart.value = sfencePart
|
|
self.dut.io_fromCSR_virtualInst_hfence.value = hfence
|
|
self.dut.io_fromCSR_virtualInst_hlsv.value = hlsv
|
|
self.dut.io_fromCSR_virtualInst_wfi.value = wfi
|
|
|
|
|
|
def Get_input_ready(self, i):
|
|
return self.input_inst[i].ready.value
|
|
|
|
def Get_allow_input_number(self):
|
|
cnt = 0
|
|
for i in range(6):
|
|
if self.input_inst[i].ready.value == 1:
|
|
cnt = i + 1
|
|
else:
|
|
break
|
|
return cnt
|
|
|
|
def Input_instruction_list(self, insts, valid):
|
|
for i in range(6):
|
|
self.Input_instruction(i, 0, 0, 0, 0, 0, 0, 0,0)
|
|
for i, inst in enumerate(insts):
|
|
self.Input_instruction(i, valid, inst[0], 0, 0, 0, 0, 0,inst[3])
|
|
|
|
def Get_decode_result(self):
|
|
insts_result = []
|
|
num = 0
|
|
for i in range(6):
|
|
if self.output_instrution[i].valid.value == 1 and self.output_instrution[i].bits_lastUop.value == 1:
|
|
insts_result.append((self.output_instrution[i].bits_instr.value, self.output_instrution[i].bits_exceptionVec_2.value or self.output_instrution[i].bits_exceptionVec_22.value, self.output_instrution[i].bits_firstUop.value))
|
|
num = num + 1
|
|
return num, insts_result
|
|
|
|
|
|
@pytest.fixture()
|
|
def decoder_fixture(request):
|
|
# before test
|
|
init_function_coverage(g)
|
|
func_name = request.node.name
|
|
# If the output directory does not exist, create it
|
|
output_dir_path = get_out_dir("decoder/log")
|
|
os.makedirs(output_dir_path, exist_ok=True)
|
|
decoder = DecodeWrapper(DUTDecodeStage(
|
|
waveform_filename=get_out_dir("decoder/decode_%s.fst"%func_name),
|
|
coverage_filename=get_out_dir("decoder/decode_%s.dat"%func_name),
|
|
))
|
|
decoder.dut.InitClock("clock")
|
|
decoder.dut.StepRis(lambda x: g.sample())
|
|
# decoder.dut.io_in_0_valid.AsImmWrite()
|
|
# decoder.dut.io_in_0_bits_instr.AsImmWrite()
|
|
yield decoder
|
|
# after test
|
|
decoder.dut.Finish()
|
|
coverage_file = get_out_dir("decoder/decode_%s.dat"%func_name)
|
|
if not os.path.exists(coverage_file):
|
|
raise FileNotFoundError(f"File not found: {coverage_file}")
|
|
set_line_coverage(request, coverage_file, get_root_dir("scripts/backend_ctrlblock_decode"))
|
|
set_func_coverage(request, g)
|
|
g.clear()
|
|
|
|
|
|
def comapre_result(ref_value_list, dut_value_list, num):
|
|
if num == 0:
|
|
return
|
|
else:
|
|
for i in range(num):
|
|
if ref_value_list[i][1] != dut_value_list[i][1]:
|
|
debug("================================")
|
|
debug(ref_value_list[i])
|
|
debug(dut_value_list[i])
|
|
|
|
log_all_info_file = None
|
|
log_err_info_file = None
|
|
|
|
def open_log_file(name):
|
|
global log_all_info_file
|
|
global log_err_info_file
|
|
# Obtain the current time and format it
|
|
current_time = datetime.datetime.now().strftime("%Y-%m-%d_%H-%M-%S")
|
|
output_dir = get_out_dir("decoder/log/log")
|
|
# Create a file name
|
|
if name is not None:
|
|
filename_all = output_dir + f"_all_{name}.txt"
|
|
filename_err = output_dir + f"_err_{name}.txt"
|
|
else:
|
|
filename_all = output_dir + f"_all_{current_time}.txt"
|
|
filename_err = output_dir + f"_err_{current_time}.txt"
|
|
log_all_info_file = get_file_logger(filename_all, format=None)
|
|
log_err_info_file = get_file_logger(filename_err, format=None)
|
|
|
|
|
|
def close_log_file():
|
|
global log_all_info_file
|
|
global log_err_info_file
|
|
if log_all_info_file is not None:
|
|
log_all_info_file = None
|
|
if log_err_info_file is not None:
|
|
log_err_info_file = None
|
|
|
|
|
|
def write_all_info_to_file(info):
|
|
if log_all_info_file is not None:
|
|
log_all_info_file.info(info)
|
|
else:
|
|
debug("remember open_log_file , close_log_file")
|
|
|
|
def write_err_info_to_file(info):
|
|
if log_err_info_file is not None:
|
|
log_err_info_file.info(info)
|
|
else:
|
|
debug("remember open_log_file , close_log_file")
|
|
|
|
|
|
# Write the comparison results to a file
|
|
# ref_value_list[i][0] = Decimal display of instructions, ref_value_list[i][1] = The reference results are used to determine whether the instruction is illegal,
|
|
# ref_value_list[i][2] = Disassembly result of the instruction, ref_value_list[i][3] = Preliminary screening for illegal instructions is conducted through the RVCExpander module
|
|
# dut_value_list[i][0] = Decimal representation of the instructions output by the Decoder module,
|
|
# dut_value_list[i][1] = The output results from the Decoder are used to determine whether anomalies exist,
|
|
# dut_value_list[i][2] = The output results from the Decoder are used to determine whether the instruction is a complex instruction
|
|
def comapre_result_in_text(ref_value_list, dut_value_list, num):
|
|
if num == 0:
|
|
return
|
|
else:
|
|
for i in range(num):
|
|
# Do not change to assert for now. First, focus on implementing the functionality. Assert directly stops execution.。
|
|
if (ref_value_list[i][2] == 0 or (ref_value_list[i][0] == dut_value_list[i][0])) and (ref_value_list[i][1] == dut_value_list[i][1]):
|
|
# print("Meets expectations:( ̄︶ ̄)↗")
|
|
good_info = f"good ----- ref: {ref_value_list[i][0]}, {ref_value_list[i][1]}, {ref_value_list[i][2]}, {ref_value_list[i][3]}"
|
|
write_all_info_to_file(good_info)
|
|
else:
|
|
# print("Not meeting expectations: <(_ _)>")
|
|
bad_info = f"bad ----- ref: {ref_value_list[i][0]}, {ref_value_list[i][1]}, {ref_value_list[i][2]}, {ref_value_list[i][3]}, old inst: {ref_value_list[i][4]}, dut: {dut_value_list[i][0]}, {dut_value_list[i][1]}, complex: {dut_value_list[i][2] == 0}"
|
|
write_all_info_to_file(bad_info)
|
|
if(ref_value_list[i][2][0] != 'v'):
|
|
write_err_info_to_file(bad_info)
|
|
|
|
# Filter out certain cases where exception can be identified through instruction itself,
|
|
# Supplement with the exception detection situation of the reference model.
|
|
def instr_filter(insn_disasm_text):
|
|
instr_opcode = insn_disasm_text.split(' ')[0]
|
|
is_except = 0
|
|
if (instr_opcode == "c.lwsp" or instr_opcode == "c.ldsp" or instr_opcode == "c.addiw"):
|
|
dst = insn_disasm_text.split()[1].split(',')[0]
|
|
if dst == "zero":
|
|
is_except = 1
|
|
elif (instr_opcode == "c.addi4spn"):
|
|
imm = insn_disasm_text.split()[3]
|
|
if imm == "0":
|
|
is_except = 1
|
|
elif (instr_opcode == "c.addi16sp"):
|
|
imm = insn_disasm_text.split()[2]
|
|
if imm == "0":
|
|
is_except = 1
|
|
elif (instr_opcode == "c.lui"):
|
|
imm = insn_disasm_text.split()[2]
|
|
if imm == "0x0":
|
|
is_except = 1
|
|
elif (instr_opcode == "c.jr"):
|
|
rs1 = insn_disasm_text.split()[1]
|
|
if rs1 == "zero":
|
|
is_except = 1
|
|
elif (instr_opcode == "c.unimp"):
|
|
is_except = 1
|
|
return is_except
|
|
|
|
|
|
# Disassemble the instruction and send it to the rvc_expand module for decoding of compressed instructions
|
|
def convert_reference_format(ref_insts, need_expand, disasm_func, disasm_free_func, disasm_arg = 0):
|
|
rvc_expand = DUTRVCExpander()
|
|
rvc_expand.io_in.AsImmWrite()
|
|
inst_list = []
|
|
for insn in ref_insts:
|
|
c_void_ptr = disasm_func(ctypes.c_uint64(insn), disasm_arg)
|
|
insn_disasm = ctypes.cast(c_void_ptr, ctypes.c_char_p).value.decode('utf-8')
|
|
disasm_free_func(c_void_ptr)
|
|
|
|
if need_expand == True:
|
|
rvc_expand.io_in.value = insn
|
|
rvc_expand.RefreshComb()
|
|
instr_ex = rvc_expand.io_ill.value
|
|
instr_bits = rvc_expand.io_out_bits.value
|
|
else:
|
|
instr_ex = 0
|
|
instr_bits = insn
|
|
|
|
if insn_disasm == "unknown":
|
|
inst_list.append((instr_bits, 1, insn_disasm, instr_ex, insn))
|
|
else:
|
|
is_excpet = instr_filter(insn_disasm)
|
|
inst_list.append((instr_bits, is_excpet, insn_disasm, instr_ex, insn))
|
|
return inst_list
|
|
|
|
|
|
# The main part of the test environment
|
|
def decode_run(decoder, inst_list, need_log_file, log_file_name = None):
|
|
if need_log_file == True:
|
|
open_log_file(log_file_name)
|
|
decoder.SetDefaultValue()
|
|
decoder.Reset()
|
|
pos = 0
|
|
detect_pos = 0
|
|
insts_length = len(inst_list)
|
|
sub_valid = 1
|
|
sub_list = inst_list[pos:pos+6]
|
|
while True:
|
|
decoder.Input_instruction_list(sub_list,sub_valid)
|
|
decoder.dut.Step(1)
|
|
allow_number = decoder.Get_allow_input_number()
|
|
if allow_number > 0:
|
|
pos = pos + allow_number
|
|
sub_list = inst_list[pos:pos+allow_number]
|
|
sub_valid = 1
|
|
num, step_result_list = decoder.Get_decode_result()
|
|
if num > 0:
|
|
if need_log_file == True:
|
|
comapre_result_in_text(inst_list[detect_pos:detect_pos+num], step_result_list, num)
|
|
else:
|
|
comapre_result(inst_list[detect_pos:detect_pos+num], step_result_list, num)
|
|
detect_pos = detect_pos + num
|
|
if pos >= insts_length:
|
|
break
|
|
decoder.dut.Step(10)
|
|
close_log_file()
|