From eac1ef80911c703aa364f1afd27e5b9564f33124 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Tue, 6 Sep 2022 02:11:41 +0530 Subject: [PATCH 1/7] refactor instruction object class --- riscv_isac/InstructionObject.py | 452 +++++++++++++++++++++++++------- riscv_isac/coverage.py | 208 ++------------- 2 files changed, 378 insertions(+), 282 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index 24dcfd2a..1fd5dc0a 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -1,101 +1,351 @@ -''' Instruction Object ''' -class instructionObject(): - ''' - Instruction object class - ''' - def __init__( - self, - instr, - instr_name, - instr_addr, - rd = None, - rs1 = None, - rs2 = None, - rs3 = None, - imm = None, - zimm = None, - csr = None, - shamt = None, - succ = None, - pred = None, - rl = None, - aq = None, - rm = None, - reg_commit = None, - csr_commit = None, - mnemonic = None - ): - - ''' - Constructor. - :param instr_name: name of instruction as accepted by a standard RISC-V assembler - :param instr_addr: pc value of the instruction - :param rd: tuple containing the register index and registerfile (x or f) that will be updated by this instruction - :param rs1: typle containing the register index and registerfilr ( x or f) that will be used as the first source operand. - :param rs2: typle containing the register index and registerfilr ( x or f) that will be used as the second source operand. - :param rs3: typle containing the register index and registerfilr ( x or f) that will be used as the third source operand. - :param imm: immediate value, if any, used by the instruction - :param csr: csr index, if any, used by the instruction - :param shamt: shift amount, if any, used by the instruction - ''' - self.instr = instr - self.instr_name = instr_name - self.instr_addr = instr_addr - self.rd = rd - self.rs1 = rs1 - self.rs2 = rs2 - self.rs3 = rs3 - self.imm = imm - self.zimm = zimm - self.csr = csr - self.shamt = shamt - self.succ = succ - self.pred = pred - self.rl = rl - self.aq = aq - self.rm = rm - self.reg_commit = reg_commit - self.csr_commit = csr_commit - self.mnemonic = mnemonic - self.is_rvp = False - self.rs1_nregs = 1 - self.rs2_nregs = 1 - self.rs3_nregs = 1 - self.rd_nregs = 1 - - - def __str__(self): - line = 'instr: '+ str(self.instr)+ ' addr: '+ str(hex(self.instr_addr)) +' instr_name: '+ str(self.instr_name) - if self.rd: - line+= ' rd: '+ str(self.rd) - if self.rs1: - line+= ' rs1: '+ str(self.rs1) - if self.rs2: - line+= ' rs2: '+ str(self.rs2) - if self.rs3: - line+= ' rs3: '+ str(self.rs3) - if self.csr: - line+= ' csr: '+ str(self.csr) - if self.imm: - line+= ' imm: '+ str(self.imm) - if self.zimm: - line+= ' zimm: '+ str(self.zimm) - if self.shamt: - line+= ' shamt: '+ str(self.shamt) - if self.succ: - line+= ' succ: '+ str(self.succ) - if self.pred: - line+= ' pred: '+ str(self.pred) - if self.rl: - line+= ' rl: '+ str(self.rl) - if self.aq: - line+= ' aq: '+ str(self.aq) - if self.rm: - line+= ' rm: '+ str(self.rm) - if self.reg_commit: - line+= ' reg_commit: '+ str(self.reg_commit) - if self.csr_commit: - line+= ' csr_commit: '+ str(self.csr_commit) - if self.mnemonic: - line+= ' mnemonic: '+ str(self.mnemonic) - return line +import struct + + +unsgn_rs1 = ['sw','sd','sh','sb','ld','lw','lwu','lh','lhu','lb', 'lbu','flw','fld','fsw','fsd',\ + 'bgeu', 'bltu', 'sltiu', 'sltu','c.lw','c.ld','c.lwsp','c.ldsp',\ + 'c.sw','c.sd','c.swsp','c.sdsp','mulhu','divu','remu','divuw',\ + 'remuw','aes64ds','aes64dsm','aes64es','aes64esm','aes64ks2',\ + 'sha256sum0','sha256sum1','sha256sig0','sha256sig1','sha512sig0',\ + 'sha512sum1r','sha512sum0r','sha512sig1l','sha512sig0l','sha512sig1h','sha512sig0h',\ + 'sha512sig1','sha512sum0','sha512sum1','sm3p0','sm3p1','aes64im',\ + 'sm4ed','sm4ks','ror','rol','rori','rorw','rolw','roriw','clmul','clmulh','clmulr',\ + 'andn','orn','xnor','pack','packh','packu','packuw','packw',\ + 'xperm.n','xperm.b','grevi','aes64ks1i', 'shfli', 'unshfli', \ + 'aes32esmi', 'aes32esi', 'aes32dsmi', 'aes32dsi','bclr','bext','binv',\ + 'bset','zext.h','sext.h','sext.b','minu','maxu','orc.b','add.uw','sh1add.uw',\ + 'sh2add.uw','sh3add.uw','slli.uw','clz','clzw','ctz','ctzw','cpop','cpopw','rev8',\ + 'bclri','bexti','binvi','bseti','fcvt.d.wu','fcvt.s.wu','fcvt.d.lu','fcvt.s.lu'] +unsgn_rs2 = ['bgeu', 'bltu', 'sltiu', 'sltu', 'sll', 'srl', 'sra','mulhu',\ + 'mulhsu','divu','remu','divuw','remuw','aes64ds','aes64dsm','aes64es',\ + 'aes64esm','aes64ks2','sm4ed','sm4ks','ror','rol','rorw','rolw','clmul',\ + 'clmulh','clmulr','andn','orn','xnor','pack','packh','packu','packuw','packw',\ + 'xperm.n','xperm.b', 'aes32esmi', 'aes32esi', 'aes32dsmi', 'aes32dsi',\ + 'sha512sum1r','sha512sum0r','sha512sig1l','sha512sig1h','sha512sig0l','sha512sig0h','fsw',\ + 'bclr','bext','binv','bset','minu','maxu','add.uw','sh1add.uw','sh2add.uw','sh3add.uw'] +f_instrs_pref = ['fadd', 'fclass', 'fcvt', 'fdiv', 'feq', 'fld', 'fle', 'flt', 'flw', 'fmadd',\ + 'fmax', 'fmin', 'fmsub', 'fmul', 'fmv', 'fnmadd', 'fnmsub', 'fsd', 'fsgnj', 'fsqrt',\ + 'fsub', 'fsw'] + + + +instr_var_evaluator_funcs = {} # dictionary for holding registered evaluator funcs +def evaluator_func(instr_var_name, cond): # decorator for registering evaluator funcs + def evaluator_func_registration_decorator(func): + if instr_var_name in instr_var_evaluator_funcs: + instr_var_evaluator_funcs[instr_var_name].append((cond, func)) + else: + instr_var_evaluator_funcs[instr_var_name] = [(cond, func)] + return func + return evaluator_func_registration_decorator + + +''' Instruction Object ''' +class instructionObject(): + ''' + Instruction object class + ''' + def __init__( + self, + instr, + instr_name, + instr_addr, + rd = None, + rs1 = None, + rs2 = None, + rs3 = None, + imm = None, + zimm = None, + csr = None, + shamt = None, + succ = None, + pred = None, + rl = None, + aq = None, + rm = None, + reg_commit = None, + csr_commit = None, + mnemonic = None + ): + + ''' + Constructor. + :param instr_name: name of instruction as accepted by a standard RISC-V assembler + :param instr_addr: pc value of the instruction + :param rd: tuple containing the register index and registerfile (x or f) that will be updated by this instruction + :param rs1: typle containing the register index and registerfilr ( x or f) that will be used as the first source operand. + :param rs2: typle containing the register index and registerfilr ( x or f) that will be used as the second source operand. + :param rs3: typle containing the register index and registerfilr ( x or f) that will be used as the third source operand. + :param imm: immediate value, if any, used by the instruction + :param csr: csr index, if any, used by the instruction + :param shamt: shift amount, if any, used by the instruction + ''' + self.instr = instr + self.instr_name = instr_name + self.instr_addr = instr_addr + self.rd = rd + self.rs1 = rs1 + self.rs2 = rs2 + self.rs3 = rs3 + self.imm = imm + self.zimm = zimm + self.csr = csr + self.shamt = shamt + self.succ = succ + self.pred = pred + self.rl = rl + self.aq = aq + self.rm = rm + self.reg_commit = reg_commit + self.csr_commit = csr_commit + self.mnemonic = mnemonic + self.is_rvp = False + self.rs1_nregs = 1 + self.rs2_nregs = 1 + self.rs3_nregs = 1 + self.rd_nregs = 1 + + + def evaluate_instr_vars(self, xlen, flen, arch_state, instr_vars): + ''' + This function populates the provided instr_vars dictionary + with the necessary fields to evaluate the coverpoints. + ''' + self._xlen = xlen + self._flen = flen + + # create signed/unsigned conversion params + if xlen == 32: + self._unsgn_sz = '>I' + self._sgn_sz = '>i' + else: + self._unsgn_sz = '>Q' + self._sgn_sz = '>q' + + self._iflen = flen + if self.instr_name.endswith(".s") or 'fmv.x.w' in self.instr_name: + self._iflen = 32 + elif self.instr_name.endswith(".d"): + self._iflen = 64 + + self._fsgn_sz = '>Q' if flen==64 else '>I' + + imm_val = instr_vars.get('imm_val', None) + + # capture the register operand values + rs1_val = self.evaluate_instr_var("rs1_val", arch_state) + rs2_val = self.evaluate_instr_var("rs2_val", arch_state) + rs3_val = self.evaluate_instr_var("rs3_val", arch_state) + + ea_align = None + # the ea_align variable is used by the eval statements of the + # coverpoints for conditional ops and memory ops + if self.instr_name in ['jal','bge','bgeu','blt','bltu','beq','bne']: + ea_align = (self.instr_addr+(imm_val<<1)) % 4 + if self.instr_name == "jalr": + ea_align = (rs1_val + imm_val) % 4 + if self.instr_name in ['sw','sh','sb','lw','lhu','lh','lb','lbu','lwu','flw','fsw']: + ea_align = (rs1_val + imm_val) % 4 + if self.instr_name in ['ld','sd','fld','fsd']: + ea_align = (rs1_val + imm_val) % 8 + + instr_vars.update({ + 'rs1_val': rs1_val, + 'rs2_val': rs2_val, + 'rs3_val': rs3_val, + 'rm_val': self.rm, + 'ea_align': ea_align, + 'xlen': self._xlen, + 'flen': self._flen, + 'iflen': self._iflen + }) + + # derived instruction variables specific to an extension + ext_specific_vars = self.evaluate_instr_var("ext_specific_vars", instr_vars, arch_state) + if ext_specific_vars is not None: + instr_vars.update(ext_specific_vars) + + + def update_arch_state(self, arch_state): + ''' + This function updates the arch state with the effect of + this instruction. + ''' + arch_state.pc = self.instr_addr + + commitvalue = self.reg_commit + if commitvalue is not None: + if self.rd[1] == 'x': + arch_state.x_rf[int(commitvalue[1])] = str(commitvalue[2][2:]) + elif self.rd[1] == 'f': + arch_state.f_rf[int(commitvalue[1])] = str(commitvalue[2][2:]) + + + def evaluate_instr_var(self, instr_var_name, *args): + for cond, func in instr_var_evaluator_funcs.get(instr_var_name, []): + if cond( + instr_name = self.instr_name, + rs1 = self.rs1, + rs2 = self.rs2, + rs3 = self.rs3, + is_rvp = self.is_rvp + ): # could just instr_name suffice? + return func(self, *args) + + return None + + + ''' + Evaluator funcs for rs1_val + ''' + @evaluator_func("rs1_val", lambda **params: params['instr_name'] in unsgn_rs1 and params['rs1'] is not None) + def evaluate_rs1_val_unsgn(self, arch_state): + return self.evaluate_reg_val_unsgn(self.rs1[0], arch_state) + + + @evaluator_func("rs1_val", lambda **params: params['is_rvp'] and params['rs1'] is not None) + def evaluate_rs1_val_p_ext(self, arch_state): + return self.evaluate_reg_val_p_ext(self.rs1[0], self.rs1_nregs, arch_state) + + + # this gets messy because disjoint conditions are needed when we use the decoders, as the order in the list might vary + @evaluator_func("rs1_val", lambda **params: not params['instr_name'] in unsgn_rs1 and not params['is_rvp'] and params['rs1'] is not None and params['rs1'][1] == 'x') + def evaluate_rs1_val_sgn(self, arch_state): + return self.evaluate_reg_val_sgn(self.rs1[0], arch_state) + + + @evaluator_func("rs1_val", lambda **params: not params['instr_name'] in unsgn_rs1 and not params['is_rvp'] and params['rs1'] is not None and params['rs1'][1] == 'f') + def evaluate_rs1_val_fsgn(self, arch_state): + return self.evaluate_reg_val_fsgn(self.rs1[0], arch_state) + + + ''' + Evaluator funcs for rs2_val + ''' + @evaluator_func("rs2_val", lambda **params: params['instr_name'] in unsgn_rs2 and params['rs2'] is not None) + def evaluate_rs2_val_unsgn(self, arch_state): + return self.evaluate_reg_val_unsgn(self.rs2[0], arch_state) + + + @evaluator_func("rs2_val", lambda **params: params['is_rvp'] and params['rs2'] is not None) + def evaluate_rs2_val_p_ext(self, arch_state): + return self.evaluate_reg_val_p_ext(self.rs2[0], self.rs2_nregs, arch_state) + + + # this gets messy because disjoint conditions are needed when we use the decoders, as the order in the list might vary + @evaluator_func("rs2_val", lambda **params: not params['instr_name'] in unsgn_rs2 and not params['is_rvp'] and params['rs2'] is not None and params['rs2'][1] == 'x') + def evaluate_rs2_val_sgn(self, arch_state): + return self.evaluate_reg_val_sgn(self.rs2[0], arch_state) + + + @evaluator_func("rs2_val", lambda **params: not params['instr_name'] in unsgn_rs2 and not params['is_rvp'] and params['rs2'] is not None and params['rs2'][1] == 'f') + def evaluate_rs2_val_fsgn(self, arch_state): + return self.evaluate_reg_val_fsgn(self.rs2[0], arch_state) + + + ''' + Evaluator funcs for rs3_val + ''' + @evaluator_func("rs3_val", lambda **params: params['rs3'] is not None and params['rs3'][1] == 'f') + def evaluate_rs3_val_fsgn(self, arch_state): + return self.evaluate_reg_val_fsgn(self.rs3[0], arch_state) + + + ''' + Evaluator funcs for extension specific variables + ''' + @evaluator_func("ext_specific_vars", lambda **params: any([params['instr_name'].startswith(pref) for pref in f_instrs_pref])) + def evaluate_f_ext_sem(self, instr_vars, arch_state): + f_ext_vars = {} + + if self.rs1 is not None and self.rs1[1] == 'f': + self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], "1", arch_state, f_ext_vars) + if self.rs2 is not None and self.rs2[1] == 'f': + self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], "2", arch_state, f_ext_vars) + if self.rs3 is not None and self.rs3[1] == 'f': + self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], "3", arch_state, f_ext_vars) + + return f_ext_vars + + + ''' + Helper functions for unpacking register values + ''' + def evaluate_reg_val_unsgn(self, reg_idx, arch_state): + return struct.unpack(self._unsgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] + + + def evaluate_reg_val_sgn(self, reg_idx, arch_state): + return struct.unpack(self._sgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] + + + def evaluate_reg_val_fsgn(self, reg_idx, arch_state): + return struct.unpack(self._fsgn_sz, bytes.fromhex(arch_state.f_rf[reg_idx]))[0] + + + def evaluate_reg_val_p_ext(self, reg_idx, nregs, arch_state): + reg_val = self.evaluate_reg_val_unsgn(reg_idx, arch_state) + if nregs == 2: + reg_hi_val = evaluate_reg_val_unsgn(reg_idx+1, arch_state) + reg_val = (reg_hi_val << 32) | reg_val + return reg_val + + + def evaluate_reg_sem_f_ext(self, reg_val, postfix, arch_state, f_ext_vars): + ''' + This function expands reg_val and defines the respective sign, exponent and mantissa correspondence + ''' + if reg_val is None: + return + + if self._iflen == 32: + e_sz = 8 + m_sz = 23 + else: + e_sz = 11 + m_sz = 52 + bin_val = ('{:0'+str(self._flen)+'b}').format(reg_val) + + if self._flen > self._iflen: + f_ext_vars['rs'+postfix+'_nan_prefix'] = int(bin_val[0:self._flen-self._iflen],2) + bin_val = bin_val[self._flen-self._iflen:] + + f_ext_vars['fs'+postfix] = int(bin_val[0], 2) + f_ext_vars['fe'+postfix] = int(bin_val[1:e_sz+1], 2) + f_ext_vars['fm'+postfix] = int(bin_val[e_sz+1:], 2) + + + def __str__(self): + line = 'instr: '+ str(self.instr)+ ' addr: '+ str(hex(self.instr_addr)) +' instr_name: '+ str(self.instr_name) + if self.rd: + line+= ' rd: '+ str(self.rd) + if self.rs1: + line+= ' rs1: '+ str(self.rs1) + if self.rs2: + line+= ' rs2: '+ str(self.rs2) + if self.rs3: + line+= ' rs3: '+ str(self.rs3) + if self.csr: + line+= ' csr: '+ str(self.csr) + if self.imm: + line+= ' imm: '+ str(self.imm) + if self.zimm: + line+= ' zimm: '+ str(self.zimm) + if self.shamt: + line+= ' shamt: '+ str(self.shamt) + if self.succ: + line+= ' succ: '+ str(self.succ) + if self.pred: + line+= ' pred: '+ str(self.pred) + if self.rl: + line+= ' rl: '+ str(self.rl) + if self.aq: + line+= ' aq: '+ str(self.aq) + if self.rm: + line+= ' rm: '+ str(self.rm) + if self.reg_commit: + line+= ' reg_commit: '+ str(self.reg_commit) + if self.csr_commit: + line+= ' csr_commit: '+ str(self.csr_commit) + if self.mnemonic: + line+= ' mnemonic: '+ str(self.mnemonic) + return line diff --git a/riscv_isac/coverage.py b/riscv_isac/coverage.py index 32c6402d..5fdf6454 100644 --- a/riscv_isac/coverage.py +++ b/riscv_isac/coverage.py @@ -26,28 +26,6 @@ import multiprocessing as mp from collections.abc import MutableMapping -unsgn_rs1 = ['sw','sd','sh','sb','ld','lw','lwu','lh','lhu','lb', 'lbu','flw','fld','fsw','fsd',\ - 'bgeu', 'bltu', 'sltiu', 'sltu','c.lw','c.ld','c.lwsp','c.ldsp',\ - 'c.sw','c.sd','c.swsp','c.sdsp','mulhu','divu','remu','divuw',\ - 'remuw','aes64ds','aes64dsm','aes64es','aes64esm','aes64ks2',\ - 'sha256sum0','sha256sum1','sha256sig0','sha256sig1','sha512sig0',\ - 'sha512sum1r','sha512sum0r','sha512sig1l','sha512sig0l','sha512sig1h','sha512sig0h',\ - 'sha512sig1','sha512sum0','sha512sum1','sm3p0','sm3p1','aes64im',\ - 'sm4ed','sm4ks','ror','rol','rori','rorw','rolw','roriw','clmul','clmulh','clmulr',\ - 'andn','orn','xnor','pack','packh','packu','packuw','packw',\ - 'xperm.n','xperm.b','grevi','aes64ks1i', 'shfli', 'unshfli', \ - 'aes32esmi', 'aes32esi', 'aes32dsmi', 'aes32dsi','bclr','bext','binv',\ - 'bset','zext.h','sext.h','sext.b','minu','maxu','orc.b','add.uw','sh1add.uw',\ - 'sh2add.uw','sh3add.uw','slli.uw','clz','clzw','ctz','ctzw','cpop','cpopw','rev8',\ - 'bclri','bexti','binvi','bseti','fcvt.d.wu','fcvt.s.wu','fcvt.d.lu','fcvt.s.lu'] -unsgn_rs2 = ['bgeu', 'bltu', 'sltiu', 'sltu', 'sll', 'srl', 'sra','mulhu',\ - 'mulhsu','divu','remu','divuw','remuw','aes64ds','aes64dsm','aes64es',\ - 'aes64esm','aes64ks2','sm4ed','sm4ks','ror','rol','rorw','rolw','clmul',\ - 'clmulh','clmulr','andn','orn','xnor','pack','packh','packu','packuw','packw',\ - 'xperm.n','xperm.b', 'aes32esmi', 'aes32esi', 'aes32dsmi', 'aes32dsi',\ - 'sha512sum1r','sha512sum0r','sha512sig1l','sha512sig1h','sha512sig0l','sha512sig0h','fsw',\ - 'bclr','bext','binv','bset','minu','maxu','add.uw','sh1add.uw','sh2add.uw','sh3add.uw'] - class cross(): BASE_REG_DICT = { 'x'+str(i) : 'x'+str(i) for i in range(32)} @@ -356,29 +334,6 @@ def __add__(self, o): return temp -def define_sem(flen, iflen, rsval, postfix,local_dict): - ''' - This function expands the rsval and defining the respective sign, exponent and mantissa correspondence - :param flen: Floating point length - :param rsval: base rs value used to expand it's respective sign, exponent and mantissa - :postfix: Register number that is part of the instruction - :local_dict: Holding the copy of all the local variables from the function calling this function - :return: The dictionary of variables with it's values - ''' - if iflen == 32: - e_sz = 8 - m_sz = 23 - else: - e_sz = 11 - m_sz = 52 - bin_val = ('{:0'+str(flen)+'b}').format(rsval) - if flen > iflen: - local_dict['rs'+postfix+'_nan_prefix'] = int(bin_val[0:flen-iflen],2) - bin_val = bin_val[flen-iflen:] - local_dict['fs'+postfix] = int(bin_val[0],2) - local_dict['fe'+postfix] = int(bin_val[1:e_sz+1],2) - local_dict['fm'+postfix] = int(bin_val[e_sz+1:],2) - def pretty_print_yaml(yaml): res = '''''' for line in ruamel.yaml.round_trip_dump(yaml, indent=5, block_seq_indent=3).splitlines(True): @@ -613,42 +568,12 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr instr = queue.get_nowait() - mnemonic = instr.mnemonic - commitvalue = instr.reg_commit - - # assign default values to operands - nxf_rs1 = 0 - nxf_rs2 = 0 - nxf_rs3 = 0 - nxf_rd = 0 - rs1_type = 'x' - rs2_type = 'x' - rs3_type = 'x' - rd_type = 'x' - - csr_addr = None - - # create signed/unsigned conversion params - if xlen == 32: - unsgn_sz = '>I' - sgn_sz = '>i' - else: - unsgn_sz = '>Q' - sgn_sz = '>q' - - iflen = flen - - if instr.instr_name.endswith(".s") or 'fmv.x.w' in instr.instr_name: - iflen = 32 - elif instr.instr_name.endswith(".d"): - iflen = 64 - - fsgn_sz = '>Q' if flen==64 else '>I' - # if instruction is empty then return if instr is None: return cgf + mnemonic = instr.mnemonic + # check if instruction lies within the valid region of interest if addr_pairs: if any([instr.instr_addr >= saddr and instr.instr_addr < eaddr for saddr,eaddr in addr_pairs]): @@ -658,76 +583,36 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr else: enable=True - # capture the operands and their values from the regfile + instr_vars = {} + + # capture the operands if instr.rs1 is not None: - rs1_type = instr.rs1[1] - rs1 = rs1_type + str(instr.rs1[0]) - nxf_rs1 = instr.rs1[0] - exec(f'{rs1} = rs1') + rs1 = instr.rs1[1] + str(instr.rs1[0]) + instr_vars['rs1'] = rs1 if instr.rs2 is not None: - rs2_type = instr.rs2[1] - rs2 = rs2_type + str(instr.rs2[0]) - nxf_rs2 = instr.rs2[0] - exec(f'{rs2} = rs2') + rs2 = instr.rs2[1] + str(instr.rs2[0]) + instr_vars['rs2'] = rs2 if instr.rs3 is not None: - rs3_type = instr.rs3[1] - rs3 = rs3_type + str(instr.rs3[0]) - nxf_rs3 = instr.rs3[0] - exec(f'{rs3} = rs3') + rs3 = instr.rs3[1] + str(instr.rs3[0]) + instr_vars['rs3'] = rs3 if instr.rd is not None: is_rd_valid = True - rd_type = instr.rd[1] - rd = rd_type + str(instr.rd[0]) - nxf_rd = instr.rd[0] - exec(f'{rd} = rd') + rd = instr.rd[1] + str(instr.rd[0]) + instr_vars['rd'] = rd else: is_rd_valid = False if instr.imm is not None: imm_val = instr.imm + instr_vars['imm_val'] = imm_val if instr.shamt is not None: imm_val = instr.shamt + instr_vars['imm_val'] = imm_val - - - instr_vars = {} - - # special value conversion based on signed/unsigned operations - rs1_val = None - if instr.instr_name in unsgn_rs1: - rs1_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs1]))[0] - elif instr.is_rvp: - rs1_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs1]))[0] - if instr.rs1_nregs == 2: - rs1_hi_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs1+1]))[0] - rs1_val = (rs1_hi_val << 32) | rs1_val - elif rs1_type == 'x': - rs1_val = struct.unpack(sgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs1]))[0] - elif rs1_type == 'f': - rs1_val = struct.unpack(fsgn_sz, bytes.fromhex(arch_state.f_rf[nxf_rs1]))[0] - define_sem(flen,iflen,rs1_val,"1",instr_vars) - - rs2_val = None - if instr.instr_name in unsgn_rs2: - rs2_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs2]))[0] - elif instr.is_rvp: - rs2_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs2]))[0] - if instr.rs2_nregs == 2: - rs2_hi_val = struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs2+1]))[0] - rs2_val = (rs2_hi_val << 32) | rs2_val - elif rs2_type == 'x': - rs2_val = struct.unpack(sgn_sz, bytes.fromhex(arch_state.x_rf[nxf_rs2]))[0] - elif rs2_type == 'f': - rs2_val = struct.unpack(fsgn_sz, bytes.fromhex(arch_state.f_rf[nxf_rs2]))[0] - define_sem(flen,iflen,rs2_val,"2",instr_vars) - - rs3_val = None - if rs3_type == 'f': - rs3_val = struct.unpack(fsgn_sz, bytes.fromhex(arch_state.f_rf[nxf_rs3]))[0] - define_sem(flen,iflen,rs3_val,"3",instr_vars) + instr.evaluate_instr_vars(xlen, flen, arch_state, instr_vars) sig_update = False if instr.instr_name in ['sh','sb','sw','sd','c.sw','c.sd','c.swsp','c.sdsp'] and sig_addrs: - store_address = rs1_val + imm_val + store_address = instr_vars['rs1_val'] + instr_vars['imm_val'] for start, end in sig_addrs: if store_address >= start and store_address <= end: sig_update = True @@ -738,45 +623,10 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr else: result_count = instr.rd_nregs - instr_vars["rm_val"] = instr.rm - instr_vars['fcsr'] = int(csr_regfile['fcsr'],16) - - arch_state.pc = instr.instr_addr + instr_vars['fcsr'] = int(csr_regfile['fcsr'], 16) - ea_align = None - # the ea_align variable is used by the eval statements of the - # coverpoints for conditional ops and memory ops - if instr.instr_name in ['jal','bge','bgeu','blt','bltu','beq','bne']: - ea_align = (instr.instr_addr+(imm_val<<1)) % 4 - - if instr.instr_name == "jalr": - ea_align = (rs1_val + imm_val) % 4 - - if instr.instr_name in ['sw','sh','sb','lw','lhu','lh','lb','lbu','lwu','flw','fsw']: - ea_align = (rs1_val + imm_val) % 4 - if instr.instr_name in ['ld','sd','fld','fsd']: - ea_align = (rs1_val + imm_val) % 8 - - if rs1_val is not None: - instr_vars['rs1_val'] = rs1_val - if rs2_val is not None: - instr_vars['rs2_val'] = rs2_val - if rs3_val is not None: - instr_vars['rs3_val'] = rs3_val - if imm_val is not None: - instr_vars['imm_val'] = imm_val - if ea_align is not None: - instr_vars['ea_align'] = ea_align - instr_vars['xlen'] = xlen - instr_vars['flen'] = flen - instr_vars['iflen'] = iflen - - local_dict = {} for i in csr_regfile.csr_regs: - local_dict[i] = int(csr_regfile[i],16) - - local_dict['xlen'] = xlen - local_dict['flen'] = flen + instr_vars[i] = int(csr_regfile[i],16) if enable : for cov_labels,value in cgf.items(): @@ -795,7 +645,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr conds = value['p_op_cond'] # Construct and evaluate conditions is_found = True - if not eval(conds): + if not eval(conds, globals(), instr_vars): is_found = False mnemonic = list(value[req_node].keys()) @@ -860,7 +710,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr if 'op_comb' in value and len(value['op_comb']) != 0 : for coverpoints in value['op_comb']: - if eval(coverpoints): + if eval(coverpoints, globals(), instr_vars): if cgf[cov_labels]['op_comb'][coverpoints] == 0: stats.ucovpt.append(str(coverpoints)) if no_count: @@ -888,7 +738,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr if 'abstract_comb' in value \ and len(value['abstract_comb']) != 0 : for coverpoints in value['abstract_comb']: - if eval(coverpoints): + if eval(coverpoints, globals(), instr_vars): if cgf[cov_labels]['abstract_comb'][coverpoints] == 0: stats.ucovpt.append(str(coverpoints)) if no_count: @@ -898,7 +748,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr if 'csr_comb' in value and len(value['csr_comb']) != 0: for coverpoints in value['csr_comb']: - if eval(coverpoints, {"__builtins__":None}, local_dict): + if eval(coverpoints, {"__builtins__":None}, instr_vars): if cgf[cov_labels]['csr_comb'][coverpoints] == 0: stats.ucovpt.append(str(coverpoints)) if no_count: @@ -909,7 +759,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr elif 'opcode' not in value: if 'csr_comb' in value and len(value['csr_comb']) != 0: for coverpoints in value['csr_comb']: - if eval(coverpoints, {"__builtins__":None}, local_dict): + if eval(coverpoints, {"__builtins__":None}, instr_vars): if cgf[cov_labels]['csr_comb'][coverpoints] == 0: stats.ucovpt.append(str(coverpoints)) if no_count: @@ -928,8 +778,8 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr stats.ucode_seq.append('[' + str(hex(instr.instr_addr)) + ']:' + instr.instr_name) if instr.instr_name in ['sh','sb','sw','sd','c.sw','c.sd','c.swsp','c.sdsp'] and sig_addrs: - store_address = rs1_val + imm_val - store_val = '0x'+arch_state.x_rf[nxf_rs2] + store_address = instr_vars['rs1_val'] + instr_vars['imm_val'] + store_val = '0x'+arch_state.x_rf[instr.rs2[0]] for start, end in sig_addrs: if store_address >= start and store_address <= end: logger.debug('Signature update : ' + str(hex(store_address))) @@ -967,11 +817,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr stats.code_seq = [] stats.ucode_seq = [] - if commitvalue is not None: - if rd_type == 'x': - arch_state.x_rf[int(commitvalue[1])] = str(commitvalue[2][2:]) - elif rd_type == 'f': - arch_state.f_rf[int(commitvalue[1])] = str(commitvalue[2][2:]) + instr.update_arch_state(arch_state) csr_commit = instr.csr_commit if csr_commit is not None: From b3e3e6e29b3260fc16e9ea808df556ca217eed0e Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Wed, 28 Sep 2022 00:52:21 +0530 Subject: [PATCH 2/7] =?UTF-8?q?Bump=20version:=200.15.0=20=E2=86=92=200.16?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 +++ riscv_isac/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5ae6aa63..e4ef46c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.16.0] - 2022-09-28 +- Refactored the instruction object class + ## [0.15.0] - 2022-08-25 - Added support for instruction aliases diff --git a/riscv_isac/__init__.py b/riscv_isac/__init__.py index 326f1a73..8aeb44e2 100644 --- a/riscv_isac/__init__.py +++ b/riscv_isac/__init__.py @@ -4,4 +4,4 @@ __author__ = """InCore Semiconductors Pvt Ltd""" __email__ = 'info@incoresemi.com' -__version__ = '0.15.0' +__version__ = '0.16.0' diff --git a/setup.cfg b/setup.cfg index 509e02dd..06dfbe58 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.15.0 +current_version = 0.16.0 commit = True tag = True diff --git a/setup.py b/setup.py index aa7538ef..3d6be568 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read_requires(): setup( name='riscv_isac', - version='0.15.0', + version='0.16.0', description="RISC-V ISAC", long_description=readme + '\n\n', classifiers=[ From 55fdaf3c63619a58757b2938c4659c79287b3510 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Thu, 29 Sep 2022 00:15:00 +0530 Subject: [PATCH 3/7] move evaluation of fcsr inside f extension specific instr vars evaluator function --- riscv_isac/InstructionObject.py | 29 ++++++++++++++++++----------- riscv_isac/coverage.py | 12 ++---------- 2 files changed, 20 insertions(+), 21 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index 1fd5dc0a..d06d298d 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -27,7 +27,6 @@ 'fsub', 'fsw'] - instr_var_evaluator_funcs = {} # dictionary for holding registered evaluator funcs def evaluator_func(instr_var_name, cond): # decorator for registering evaluator funcs def evaluator_func_registration_decorator(func): @@ -105,7 +104,7 @@ def __init__( self.rd_nregs = 1 - def evaluate_instr_vars(self, xlen, flen, arch_state, instr_vars): + def evaluate_instr_vars(self, xlen, flen, arch_state, csr_regfile, instr_vars): ''' This function populates the provided instr_vars dictionary with the necessary fields to evaluate the coverpoints. @@ -160,15 +159,15 @@ def evaluate_instr_vars(self, xlen, flen, arch_state, instr_vars): }) # derived instruction variables specific to an extension - ext_specific_vars = self.evaluate_instr_var("ext_specific_vars", instr_vars, arch_state) + ext_specific_vars = self.evaluate_instr_var("ext_specific_vars", instr_vars, arch_state, csr_regfile) if ext_specific_vars is not None: instr_vars.update(ext_specific_vars) - def update_arch_state(self, arch_state): + def update_arch_state(self, arch_state, csr_regfile): ''' - This function updates the arch state with the effect of - this instruction. + This function updates the arch state and csr regfiles + with the effect of this instruction. ''' arch_state.pc = self.instr_addr @@ -179,6 +178,12 @@ def update_arch_state(self, arch_state): elif self.rd[1] == 'f': arch_state.f_rf[int(commitvalue[1])] = str(commitvalue[2][2:]) + csr_commit = self.csr_commit + if csr_commit is not None: + for commits in csr_commit: + if (commits[0] == "CSR"): + csr_regfile[commits[1]] = str(commits[2][2:]) + def evaluate_instr_var(self, instr_var_name, *args): for cond, func in instr_var_evaluator_funcs.get(instr_var_name, []): @@ -254,15 +259,17 @@ def evaluate_rs3_val_fsgn(self, arch_state): Evaluator funcs for extension specific variables ''' @evaluator_func("ext_specific_vars", lambda **params: any([params['instr_name'].startswith(pref) for pref in f_instrs_pref])) - def evaluate_f_ext_sem(self, instr_vars, arch_state): + def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): f_ext_vars = {} + f_ext_vars['fcsr'] = int(csr_regfile['fcsr'], 16) + if self.rs1 is not None and self.rs1[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], "1", arch_state, f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], "1", f_ext_vars) if self.rs2 is not None and self.rs2[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], "2", arch_state, f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], "2", f_ext_vars) if self.rs3 is not None and self.rs3[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], "3", arch_state, f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], "3", f_ext_vars) return f_ext_vars @@ -290,7 +297,7 @@ def evaluate_reg_val_p_ext(self, reg_idx, nregs, arch_state): return reg_val - def evaluate_reg_sem_f_ext(self, reg_val, postfix, arch_state, f_ext_vars): + def evaluate_reg_sem_f_ext(self, reg_val, postfix, f_ext_vars): ''' This function expands reg_val and defines the respective sign, exponent and mantissa correspondence ''' diff --git a/riscv_isac/coverage.py b/riscv_isac/coverage.py index 5fdf6454..058f6059 100644 --- a/riscv_isac/coverage.py +++ b/riscv_isac/coverage.py @@ -608,7 +608,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr imm_val = instr.shamt instr_vars['imm_val'] = imm_val - instr.evaluate_instr_vars(xlen, flen, arch_state, instr_vars) + instr.evaluate_instr_vars(xlen, flen, arch_state, csr_regfile, instr_vars) sig_update = False if instr.instr_name in ['sh','sb','sw','sd','c.sw','c.sd','c.swsp','c.sdsp'] and sig_addrs: @@ -623,8 +623,6 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr else: result_count = instr.rd_nregs - instr_vars['fcsr'] = int(csr_regfile['fcsr'], 16) - for i in csr_regfile.csr_regs: instr_vars[i] = int(csr_regfile[i],16) @@ -817,13 +815,7 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr stats.code_seq = [] stats.ucode_seq = [] - instr.update_arch_state(arch_state) - - csr_commit = instr.csr_commit - if csr_commit is not None: - for commits in csr_commit: - if(commits[0]=="CSR"): - csr_regfile[commits[1]] = str(commits[2][2:]) + instr.update_arch_state(arch_state, csr_regfile) # Remove hit coverpoints if no_count is set if no_count: From 085824cc913afecbfe224139038fab8083f7f300 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Thu, 29 Sep 2022 01:01:58 +0530 Subject: [PATCH 4/7] add docstrings for the newly added member methods in the instruction object class --- riscv_isac/InstructionObject.py | 42 +++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 5 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index d06d298d..8f4d6061 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -28,7 +28,15 @@ instr_var_evaluator_funcs = {} # dictionary for holding registered evaluator funcs -def evaluator_func(instr_var_name, cond): # decorator for registering evaluator funcs +def evaluator_func(instr_var_name, cond): + ''' + This is a parametrized decorator for registering the evaluator funcs used for + evaluating an instruction variable (instruction variables are used in the + evaluation of coverpoints). + + :param instr_var_name: Name of the instruction variable + :param cond: A boolean lambda function evaluating to True if the decorated function needs to be selected for the evaluation of the instruction variable + ''' def evaluator_func_registration_decorator(func): if instr_var_name in instr_var_evaluator_funcs: instr_var_evaluator_funcs[instr_var_name].append((cond, func)) @@ -108,6 +116,12 @@ def evaluate_instr_vars(self, xlen, flen, arch_state, csr_regfile, instr_vars): ''' This function populates the provided instr_vars dictionary with the necessary fields to evaluate the coverpoints. + + :param xlen: Max xlen of the trace + :param flen: Max flen of the trace + :param arch_state: Architectural state + :param csr_regfile: Architectural state of CSR register files + :param instr_vars: Dictionary to be populated by the evaluated instruction variables ''' self._xlen = xlen self._flen = flen @@ -168,6 +182,9 @@ def update_arch_state(self, arch_state, csr_regfile): ''' This function updates the arch state and csr regfiles with the effect of this instruction. + + :param csr_regfile: Architectural state of CSR register files + :param instr_vars: Dictionary to be populated by the evaluated instruction variables ''' arch_state.pc = self.instr_addr @@ -186,6 +203,13 @@ def update_arch_state(self, arch_state, csr_regfile): def evaluate_instr_var(self, instr_var_name, *args): + ''' + This function selects and invokes the appropriate function for evaluating + the given instruction variable by running the condition lambda functions + of the registered evaluator functions. + + :param instr_var_name: Name of the instruction variable + ''' for cond, func in instr_var_evaluator_funcs.get(instr_var_name, []): if cond( instr_name = self.instr_name, @@ -201,6 +225,8 @@ def evaluate_instr_var(self, instr_var_name, *args): ''' Evaluator funcs for rs1_val + + :param arch_state: Architectural state ''' @evaluator_func("rs1_val", lambda **params: params['instr_name'] in unsgn_rs1 and params['rs1'] is not None) def evaluate_rs1_val_unsgn(self, arch_state): @@ -212,7 +238,6 @@ def evaluate_rs1_val_p_ext(self, arch_state): return self.evaluate_reg_val_p_ext(self.rs1[0], self.rs1_nregs, arch_state) - # this gets messy because disjoint conditions are needed when we use the decoders, as the order in the list might vary @evaluator_func("rs1_val", lambda **params: not params['instr_name'] in unsgn_rs1 and not params['is_rvp'] and params['rs1'] is not None and params['rs1'][1] == 'x') def evaluate_rs1_val_sgn(self, arch_state): return self.evaluate_reg_val_sgn(self.rs1[0], arch_state) @@ -225,6 +250,8 @@ def evaluate_rs1_val_fsgn(self, arch_state): ''' Evaluator funcs for rs2_val + + :param arch_state: Architectural state ''' @evaluator_func("rs2_val", lambda **params: params['instr_name'] in unsgn_rs2 and params['rs2'] is not None) def evaluate_rs2_val_unsgn(self, arch_state): @@ -236,7 +263,6 @@ def evaluate_rs2_val_p_ext(self, arch_state): return self.evaluate_reg_val_p_ext(self.rs2[0], self.rs2_nregs, arch_state) - # this gets messy because disjoint conditions are needed when we use the decoders, as the order in the list might vary @evaluator_func("rs2_val", lambda **params: not params['instr_name'] in unsgn_rs2 and not params['is_rvp'] and params['rs2'] is not None and params['rs2'][1] == 'x') def evaluate_rs2_val_sgn(self, arch_state): return self.evaluate_reg_val_sgn(self.rs2[0], arch_state) @@ -249,6 +275,8 @@ def evaluate_rs2_val_fsgn(self, arch_state): ''' Evaluator funcs for rs3_val + + :param arch_state: Architectural state ''' @evaluator_func("rs3_val", lambda **params: params['rs3'] is not None and params['rs3'][1] == 'f') def evaluate_rs3_val_fsgn(self, arch_state): @@ -257,6 +285,10 @@ def evaluate_rs3_val_fsgn(self, arch_state): ''' Evaluator funcs for extension specific variables + + :param instr_vars: Dictionary of instruction variables already evaluated + :param arch_state: Architectural state + :param csr_regfile: Architectural state of CSR register files ''' @evaluator_func("ext_specific_vars", lambda **params: any([params['instr_name'].startswith(pref) for pref in f_instrs_pref])) def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): @@ -275,7 +307,7 @@ def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): ''' - Helper functions for unpacking register values + Helper functions for unpacking register values and derived fields ''' def evaluate_reg_val_unsgn(self, reg_idx, arch_state): return struct.unpack(self._unsgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] @@ -299,7 +331,7 @@ def evaluate_reg_val_p_ext(self, reg_idx, nregs, arch_state): def evaluate_reg_sem_f_ext(self, reg_val, postfix, f_ext_vars): ''' - This function expands reg_val and defines the respective sign, exponent and mantissa correspondence + This function expands reg_val and defines the respective sign, exponent and mantissa components ''' if reg_val is None: return From a587f0ed82b37c2642188895eee49e5d07cdfd2a Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Thu, 29 Sep 2022 12:44:31 +0530 Subject: [PATCH 5/7] propagate xlen, flen and iflen through instr_vars only rather than making them private instance variables --- riscv_isac/InstructionObject.py | 97 +++++++++++++++------------------ 1 file changed, 45 insertions(+), 52 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index 8f4d6061..10dd1097 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -123,31 +123,21 @@ def evaluate_instr_vars(self, xlen, flen, arch_state, csr_regfile, instr_vars): :param csr_regfile: Architectural state of CSR register files :param instr_vars: Dictionary to be populated by the evaluated instruction variables ''' - self._xlen = xlen - self._flen = flen + instr_vars['xlen'] = xlen + instr_vars['flen'] = flen - # create signed/unsigned conversion params - if xlen == 32: - self._unsgn_sz = '>I' - self._sgn_sz = '>i' - else: - self._unsgn_sz = '>Q' - self._sgn_sz = '>q' - - self._iflen = flen + instr_vars['iflen'] = flen if self.instr_name.endswith(".s") or 'fmv.x.w' in self.instr_name: - self._iflen = 32 + instr_vars['iflen'] = 32 elif self.instr_name.endswith(".d"): - self._iflen = 64 - - self._fsgn_sz = '>Q' if flen==64 else '>I' + instr_vars['iflen'] = 64 imm_val = instr_vars.get('imm_val', None) # capture the register operand values - rs1_val = self.evaluate_instr_var("rs1_val", arch_state) - rs2_val = self.evaluate_instr_var("rs2_val", arch_state) - rs3_val = self.evaluate_instr_var("rs3_val", arch_state) + rs1_val = self.evaluate_instr_var("rs1_val", instr_vars, arch_state) + rs2_val = self.evaluate_instr_var("rs2_val", instr_vars, arch_state) + rs3_val = self.evaluate_instr_var("rs3_val", instr_vars, arch_state) ea_align = None # the ea_align variable is used by the eval statements of the @@ -167,9 +157,6 @@ def evaluate_instr_vars(self, xlen, flen, arch_state, csr_regfile, instr_vars): 'rs3_val': rs3_val, 'rm_val': self.rm, 'ea_align': ea_align, - 'xlen': self._xlen, - 'flen': self._flen, - 'iflen': self._iflen }) # derived instruction variables specific to an extension @@ -227,60 +214,63 @@ def evaluate_instr_var(self, instr_var_name, *args): Evaluator funcs for rs1_val :param arch_state: Architectural state + :param instr_vars: Dictionary of instruction variables already evaluated ''' @evaluator_func("rs1_val", lambda **params: params['instr_name'] in unsgn_rs1 and params['rs1'] is not None) - def evaluate_rs1_val_unsgn(self, arch_state): - return self.evaluate_reg_val_unsgn(self.rs1[0], arch_state) + def evaluate_rs1_val_unsgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_unsgn(self.rs1[0], instr_vars['xlen'], arch_state) @evaluator_func("rs1_val", lambda **params: params['is_rvp'] and params['rs1'] is not None) - def evaluate_rs1_val_p_ext(self, arch_state): + def evaluate_rs1_val_p_ext(self, instr_vars, arch_state): return self.evaluate_reg_val_p_ext(self.rs1[0], self.rs1_nregs, arch_state) @evaluator_func("rs1_val", lambda **params: not params['instr_name'] in unsgn_rs1 and not params['is_rvp'] and params['rs1'] is not None and params['rs1'][1] == 'x') - def evaluate_rs1_val_sgn(self, arch_state): - return self.evaluate_reg_val_sgn(self.rs1[0], arch_state) + def evaluate_rs1_val_sgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_sgn(self.rs1[0], instr_vars['xlen'], arch_state) @evaluator_func("rs1_val", lambda **params: not params['instr_name'] in unsgn_rs1 and not params['is_rvp'] and params['rs1'] is not None and params['rs1'][1] == 'f') - def evaluate_rs1_val_fsgn(self, arch_state): - return self.evaluate_reg_val_fsgn(self.rs1[0], arch_state) + def evaluate_rs1_val_fsgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_fsgn(self.rs1[0], instr_vars['flen'], arch_state) ''' Evaluator funcs for rs2_val :param arch_state: Architectural state + :param instr_vars: Dictionary of instruction variables already evaluated ''' @evaluator_func("rs2_val", lambda **params: params['instr_name'] in unsgn_rs2 and params['rs2'] is not None) - def evaluate_rs2_val_unsgn(self, arch_state): - return self.evaluate_reg_val_unsgn(self.rs2[0], arch_state) + def evaluate_rs2_val_unsgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_unsgn(self.rs2[0], instr_vars['xlen'], arch_state) @evaluator_func("rs2_val", lambda **params: params['is_rvp'] and params['rs2'] is not None) - def evaluate_rs2_val_p_ext(self, arch_state): + def evaluate_rs2_val_p_ext(self, instr_vars, arch_state): return self.evaluate_reg_val_p_ext(self.rs2[0], self.rs2_nregs, arch_state) @evaluator_func("rs2_val", lambda **params: not params['instr_name'] in unsgn_rs2 and not params['is_rvp'] and params['rs2'] is not None and params['rs2'][1] == 'x') - def evaluate_rs2_val_sgn(self, arch_state): - return self.evaluate_reg_val_sgn(self.rs2[0], arch_state) + def evaluate_rs2_val_sgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_sgn(self.rs2[0], instr_vars['xlen'], arch_state) @evaluator_func("rs2_val", lambda **params: not params['instr_name'] in unsgn_rs2 and not params['is_rvp'] and params['rs2'] is not None and params['rs2'][1] == 'f') - def evaluate_rs2_val_fsgn(self, arch_state): - return self.evaluate_reg_val_fsgn(self.rs2[0], arch_state) + def evaluate_rs2_val_fsgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_fsgn(self.rs2[0], instr_vars['flen'], arch_state) ''' Evaluator funcs for rs3_val :param arch_state: Architectural state + :param instr_vars: Dictionary of instruction variables already evaluated ''' @evaluator_func("rs3_val", lambda **params: params['rs3'] is not None and params['rs3'][1] == 'f') - def evaluate_rs3_val_fsgn(self, arch_state): - return self.evaluate_reg_val_fsgn(self.rs3[0], arch_state) + def evaluate_rs3_val_fsgn(self, instr_vars, arch_state): + return self.evaluate_reg_val_fsgn(self.rs3[0], instr_vars['flen'], arch_state) ''' @@ -297,11 +287,11 @@ def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): f_ext_vars['fcsr'] = int(csr_regfile['fcsr'], 16) if self.rs1 is not None and self.rs1[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], "1", f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], instr_vars['flen'], instr_vars['iflen'], "1", f_ext_vars) if self.rs2 is not None and self.rs2[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], "2", f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], instr_vars['flen'], instr_vars['iflen'], "2", f_ext_vars) if self.rs3 is not None and self.rs3[1] == 'f': - self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], "3", f_ext_vars) + self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], instr_vars['flen'], instr_vars['iflen'], "3", f_ext_vars) return f_ext_vars @@ -309,16 +299,19 @@ def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): ''' Helper functions for unpacking register values and derived fields ''' - def evaluate_reg_val_unsgn(self, reg_idx, arch_state): - return struct.unpack(self._unsgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] + def evaluate_reg_val_unsgn(self, reg_idx, xlen, arch_state): + unsgn_sz = '>I' if xlen == 32 else '>Q' + return struct.unpack(unsgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] - def evaluate_reg_val_sgn(self, reg_idx, arch_state): - return struct.unpack(self._sgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] + def evaluate_reg_val_sgn(self, reg_idx, xlen, arch_state): + sgn_sz = '>i' if xlen == 32 else '>q' + return struct.unpack(sgn_sz, bytes.fromhex(arch_state.x_rf[reg_idx]))[0] - def evaluate_reg_val_fsgn(self, reg_idx, arch_state): - return struct.unpack(self._fsgn_sz, bytes.fromhex(arch_state.f_rf[reg_idx]))[0] + def evaluate_reg_val_fsgn(self, reg_idx, flen, arch_state): + fsgn_sz = '>Q' if flen == 64 else '>I' + return struct.unpack(fsgn_sz, bytes.fromhex(arch_state.f_rf[reg_idx]))[0] def evaluate_reg_val_p_ext(self, reg_idx, nregs, arch_state): @@ -329,24 +322,24 @@ def evaluate_reg_val_p_ext(self, reg_idx, nregs, arch_state): return reg_val - def evaluate_reg_sem_f_ext(self, reg_val, postfix, f_ext_vars): + def evaluate_reg_sem_f_ext(self, reg_val, flen, iflen, postfix, f_ext_vars): ''' This function expands reg_val and defines the respective sign, exponent and mantissa components ''' if reg_val is None: return - if self._iflen == 32: + if iflen == 32: e_sz = 8 m_sz = 23 else: e_sz = 11 m_sz = 52 - bin_val = ('{:0'+str(self._flen)+'b}').format(reg_val) + bin_val = ('{:0'+str(flen)+'b}').format(reg_val) - if self._flen > self._iflen: - f_ext_vars['rs'+postfix+'_nan_prefix'] = int(bin_val[0:self._flen-self._iflen],2) - bin_val = bin_val[self._flen-self._iflen:] + if flen > iflen: + f_ext_vars['rs'+postfix+'_nan_prefix'] = int(bin_val[0:flen-iflen],2) + bin_val = bin_val[flen-iflen:] f_ext_vars['fs'+postfix] = int(bin_val[0], 2) f_ext_vars['fe'+postfix] = int(bin_val[1:e_sz+1], 2) From 46302f7eb30a1af1b389fb50ce8c04ffd221737c Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Thu, 29 Sep 2022 12:58:22 +0530 Subject: [PATCH 6/7] move the logic for capturing operands inside the instruction object class --- riscv_isac/InstructionObject.py | 14 ++++++++++++++ riscv_isac/coverage.py | 31 ++++++++++--------------------- 2 files changed, 24 insertions(+), 21 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index 10dd1097..daa1c256 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -132,6 +132,20 @@ def evaluate_instr_vars(self, xlen, flen, arch_state, csr_regfile, instr_vars): elif self.instr_name.endswith(".d"): instr_vars['iflen'] = 64 + # capture the operands + if self.rs1 is not None: + instr_vars['rs1'] = self.rs1[1] + str(self.rs1[0]) + if self.rs2 is not None: + instr_vars['rs2'] = self.rs2[1] + str(self.rs2[0]) + if self.rs3 is not None: + instr_vars['rs3'] = self.rs3[1] + str(self.rs3[0]) + if self.rd is not None: + instr_vars['rd'] = self.rd[1] + str(self.rd[0]) + if self.imm is not None: + instr_vars['imm_val'] = self.imm + if self.shamt is not None: + instr_vars['imm_val'] = self.shamt + imm_val = instr_vars.get('imm_val', None) # capture the register operand values diff --git a/riscv_isac/coverage.py b/riscv_isac/coverage.py index 058f6059..b4c9633e 100644 --- a/riscv_isac/coverage.py +++ b/riscv_isac/coverage.py @@ -585,30 +585,19 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr instr_vars = {} - # capture the operands - if instr.rs1 is not None: - rs1 = instr.rs1[1] + str(instr.rs1[0]) - instr_vars['rs1'] = rs1 - if instr.rs2 is not None: - rs2 = instr.rs2[1] + str(instr.rs2[0]) - instr_vars['rs2'] = rs2 - if instr.rs3 is not None: - rs3 = instr.rs3[1] + str(instr.rs3[0]) - instr_vars['rs3'] = rs3 - if instr.rd is not None: + instr.evaluate_instr_vars(xlen, flen, arch_state, csr_regfile, instr_vars) + + if 'rs1' in instr_vars: + rs1 = instr_vars['rs1'] + if 'rs2' in instr_vars: + rs2 = instr_vars['rs2'] + if 'rs3' in instr_vars: + rs3 = instr_vars['rs3'] + if 'rd' in instr_vars: is_rd_valid = True - rd = instr.rd[1] + str(instr.rd[0]) - instr_vars['rd'] = rd + rd = instr_vars['rd'] else: is_rd_valid = False - if instr.imm is not None: - imm_val = instr.imm - instr_vars['imm_val'] = imm_val - if instr.shamt is not None: - imm_val = instr.shamt - instr_vars['imm_val'] = imm_val - - instr.evaluate_instr_vars(xlen, flen, arch_state, csr_regfile, instr_vars) sig_update = False if instr.instr_name in ['sh','sb','sw','sd','c.sw','c.sd','c.swsp','c.sdsp'] and sig_addrs: From 0639dea63be81e14bed76c01d4736298e8ec4a96 Mon Sep 17 00:00:00 2001 From: Priyansh Rathi Date: Thu, 29 Sep 2022 22:10:26 +0530 Subject: [PATCH 7/7] evaluate f ext vars only using already evaluated instr vars --- riscv_isac/InstructionObject.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv_isac/InstructionObject.py b/riscv_isac/InstructionObject.py index daa1c256..2490257a 100644 --- a/riscv_isac/InstructionObject.py +++ b/riscv_isac/InstructionObject.py @@ -300,11 +300,11 @@ def evaluate_f_ext_sem(self, instr_vars, arch_state, csr_regfile): f_ext_vars['fcsr'] = int(csr_regfile['fcsr'], 16) - if self.rs1 is not None and self.rs1[1] == 'f': + if 'rs1' in instr_vars and instr_vars['rs1'] is not None and instr_vars['rs1'].startswith('f'): self.evaluate_reg_sem_f_ext(instr_vars['rs1_val'], instr_vars['flen'], instr_vars['iflen'], "1", f_ext_vars) - if self.rs2 is not None and self.rs2[1] == 'f': + if 'rs2' in instr_vars and instr_vars['rs2'] is not None and instr_vars['rs2'].startswith('f'): self.evaluate_reg_sem_f_ext(instr_vars['rs2_val'], instr_vars['flen'], instr_vars['iflen'], "2", f_ext_vars) - if self.rs3 is not None and self.rs3[1] == 'f': + if 'rs3' in instr_vars and instr_vars['rs3'] is not None and instr_vars['rs3'].startswith('f'): self.evaluate_reg_sem_f_ext(instr_vars['rs3_val'], instr_vars['flen'], instr_vars['iflen'], "3", f_ext_vars) return f_ext_vars