diff --git a/CHANGELOG.md b/CHANGELOG.md index 29b8bbdc..5ae6aa63 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.15.0] - 2022-08-25 +- Added support for instruction aliases + ## [0.14.0] - 2022-08-08 - Add fields to instruction object - Enable generic coverage evaluation mechanisms for floating point instructions diff --git a/docs/source/cgf.rst b/docs/source/cgf.rst index 2372a6d3..25a48143 100644 --- a/docs/source/cgf.rst +++ b/docs/source/cgf.rst @@ -376,6 +376,8 @@ A covergroup contains the following nodes: This string is divided into three parts - opcode list, assign list and condition list separated by :: symbol. It is parsed and all the three lists are obtained separately. The variables available for use in the expression are as follows: * ``instr_name`` : The instruction names in the opcode list + + * ``instruction_alias``: The instruction alias for a set of instructions as defined in ``/riscv_isac/data/instr_alias.yaml`` * ``rs1`` : The register number of source register 1 of the current instruction in the assign list. @@ -383,6 +385,7 @@ A covergroup contains the following nodes: * ``rd`` : The register number of destination register of the current instruction in the assign list. + Instruction aliases when used will be expanded into a tuple of instruction under the given alias. Along with the above mentioned variable any valid python comparison operators can be used in the condition list. @@ -401,7 +404,7 @@ A covergroup contains the following nodes: .. code-block:: python - [(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ] + [(add,sub) : rv32i_arith : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ] 3. WAW for an add instruction followed by a subtract instruction with 3 non-consuming instructions in between. diff --git a/riscv_isac/__init__.py b/riscv_isac/__init__.py index b3fa5993..326f1a73 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.14.0' +__version__ = '0.15.0' diff --git a/riscv_isac/cgf_normalize.py b/riscv_isac/cgf_normalize.py index 7224f018..0c986211 100644 --- a/riscv_isac/cgf_normalize.py +++ b/riscv_isac/cgf_normalize.py @@ -1,5 +1,6 @@ # See LICENSE.incore for details from math import * +import pprint import riscv_isac.utils as utils import itertools import random @@ -579,6 +580,26 @@ def expand_cgf(cgf_files, xlen,flen): if len(cgf[labels]['mnemonics'].keys()) > 1: logger.error(f'Multiple instruction mnemonics found when base_op label defined in {labels} label.') + # Substitute instruction aliases with equivalent tuple of instructions + if 'cross_comb' in cats: + temp = cats['cross_comb'] + + for covp, covge in dict(temp).items(): + data = covp.split('::') + ops = data[0].replace(' ', '')[1:-1].split(':') + # Substitute with tuple of instructions + for i in range(len(ops)): + exp_alias = utils.import_instr_alias(ops[i]) + if exp_alias != None: + ops[i] = tuple(exp_alias).__str__().replace("'", '').replace(" ", '') + + data[0] = '[' + ':'.join(ops) + ']' + data = '::'.join(data) + del temp[covp] + temp[data] = covge + + cgf[labels].insert(1, 'cross_comb', temp) + l = len(cats.items()) i = 0 for label,node in cats.items(): diff --git a/riscv_isac/coverage.py b/riscv_isac/coverage.py index bfdfacd1..32c6402d 100644 --- a/riscv_isac/coverage.py +++ b/riscv_isac/coverage.py @@ -50,6 +50,8 @@ class cross(): + BASE_REG_DICT = { 'x'+str(i) : 'x'+str(i) for i in range(32)} + def __init__(self,label,coverpoint): self.label = label @@ -58,12 +60,11 @@ def __init__(self,label,coverpoint): ## Extract relevant information from coverpt self.data = self.coverpoint.split('::') - self.ops = [i for i in self.data[0][1:-1].split(':')] - self.assign_lst = [i for i in self.data[1][1:-1].split(':')] - self.cond_lst = [i for i in self.data[2][1:-1].split(':')] + self.ops = self.data[0].replace(' ', '')[1:-1].split(':') + self.assign_lst = self.data[1].replace(' ', '')[1:-1].split(':') + self.cond_lst = self.data[2].lstrip().rstrip()[1:-1].split(':') def process(self, queue, window_size, addr_pairs): - ''' Check whether the coverpoint is a hit or not and update the metric ''' @@ -71,11 +72,12 @@ def process(self, queue, window_size, addr_pairs): return for index in range(len(self.ops)): + instr = queue[index] instr_name = instr.instr_name if addr_pairs: if not (any([instr.instr_addr >= saddr and instr.instr_addr < eaddr for saddr,eaddr in addr_pairs])): - continue + break rd = None rs1 = None @@ -92,13 +94,13 @@ def process(self, queue, window_size, addr_pairs): rm = None if instr.rd is not None: - rd = int(instr.rd[0]) + rd = instr.rd[1] + str(instr.rd[0]) if instr.rs1 is not None: - rs1 = int(instr.rs1[0]) + rs1 = instr.rs1[1] + str(instr.rs1[0]) if instr.rs2 is not None: - rs2 = int(instr.rs2[0]) + rs2 = instr.rs2[1] + str(instr.rs2[0]) if instr.rs3 is not None: - rs3 = int(instr.rs3[0]) + rs3 = instr.rs3[1] + str(instr.rs3[0]) if instr.imm is not None: imm = int(instr.imm) if instr.zimm is not None: @@ -118,19 +120,24 @@ def process(self, queue, window_size, addr_pairs): if instr.rm is not None: rm = int(instr.rm) - - if(self.ops[index] != '?'): - check_lst = [i for i in self.ops[index][1:-1].split(',')] + if self.ops[index].find('?') == -1: + # Handle instruction tuple + if self.ops[index].find('(') != -1: + check_lst = self.ops[index].replace('(', '').replace(')', '').split(',') + else: + check_lst = [self.ops[index]] if (instr_name not in check_lst): break - if (self.cond_lst[index] != '?'): - if(eval(self.cond_lst[index])): + + if self.cond_lst[index].find('?') == -1: + if(eval(self.cond_lst[index], locals(), cross.BASE_REG_DICT)): if(index==len(self.ops)-1): self.result = self.result + 1 else: break - if(self.assign_lst[index] != '?'): - exec(self.assign_lst[index]) + + if self.assign_lst[index].find('?') == -1: + exec(self.assign_lst[index], locals(), cross.BASE_REG_DICT) def get_metric(self): return self.result @@ -610,14 +617,14 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr commitvalue = instr.reg_commit # assign default values to operands - nxf_rs1 = None - nxf_rs2 = None - nxf_rs3 = None - nxf_rd = None - rs1_type = None - rs2_type = None - rs3_type = None - rd_type = None + 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 @@ -956,7 +963,6 @@ def compute_per_line(queue, event, cgf_queue, stats_queue, cgf, xlen, flen, addr store_val) logger.error(_log) stats.stat4.append(_log + '\n\n') - stats.covpt = [] stats.code_seq = [] stats.ucode_seq = [] @@ -1011,7 +1017,7 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle cgf = temp # If cgf does not have the covergroup pertaining to the cover-label, throw error - # and exit + # and exit if not cgf: logger.err('Covergroup(s) for ' + str(cov_labels) + ' not found') sys.exit(1) @@ -1067,7 +1073,6 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle iterator = iter(parser.__iter__()[0]) - # If number of processes to be spawned is more than that available, # allot number of processes to be equal to one less than maximum available_cores = mp.cpu_count() @@ -1104,6 +1109,7 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle ) ) ) + #Start each processes for each in process_list: each.start() @@ -1126,6 +1132,8 @@ def compute(trace_file, test_name, cgf, parser_name, decoder_name, detailed, xle obj_dict[(label,coverpt)].process(cross_cover_queue, window_size,addr_pairs) cross_cover_queue.pop(0) + + # Close all instruction queues for each in queue_list: each.close() diff --git a/riscv_isac/data/instr_alias.yaml b/riscv_isac/data/instr_alias.yaml new file mode 100644 index 00000000..a005daf2 --- /dev/null +++ b/riscv_isac/data/instr_alias.yaml @@ -0,0 +1,93 @@ +# This file holds aliases for groups of instructions + +rv32i_arith_reg: &rv32i_arith_reg + - add + - sub + - slt + - sltu + - xor + - or + - and + +rv32i_arith_imm: &rv32i_arith_imm + - addi + - slti + - sltiu + - xori + - ori + - andi + +rv32i_shift_reg: &rv32i_shift_reg + - sll + - srl + - sra + +rv32i_shift_imm: &rv32i_shift_imm + - slli + - srli + - srai + +rv32i_arith: &rv32i_arith [*rv32i_arith_reg, *rv32i_arith_imm] + +rv32i_shift: &rv32i_shift [*rv32i_shift_reg, *rv32i_shift_imm] + +rv64i_arith_reg: &rv64i_arith_reg + - *rv32i_arith_reg + - addw + - subw + +rv64i_arith_imm: &rv64i_arith_imm + - *rv32i_arith_imm + - addiw + +rv64i_shift_reg: &rv64i_shift_reg + - *rv32i_shift_reg + - sllw + - srlw + - sraw + +rv64i_shift_imm: &rv64i_shift_imm + - *rv32i_shift_imm + - slliw + - srliw + +rv64i_arith: &rv64i_arith [*rv64i_arith_reg, *rv64i_arith_imm] + +rv64i_shift: &rv64i_shift [*rv64i_shift_reg, *rv64i_shift_imm] + +rv32i_branch: &rv32i_branch + - beq + - bge + - bgeu + - blt + - bltu + - bne + +rv64i_branch: &rv64i_branch [*rv32i_branch] + +rv32i_jal: &rv32i_jal + - jal + - jalr + +rv64i_jal: &rv64i_jal [*rv32i_jal] + +rv32i_load: &rv32i_load + - lw + - lhu + - lh + - lbu + - lb + +rv64i_load: &rv364i_load + - *rv32i_load + - ld + - lwu + +rv32i_store: &rv32i_store + - sw + - sh + - sb + +rv64i_store: &rv64i_store + - *rv32i_store + - sd \ No newline at end of file diff --git a/riscv_isac/utils.py b/riscv_isac/utils.py index 83dbdcd6..8e655b84 100644 --- a/riscv_isac/utils.py +++ b/riscv_isac/utils.py @@ -5,6 +5,7 @@ import os import subprocess import shlex +import riscv_isac from riscv_isac.log import logger import ruamel from ruamel.yaml import YAML @@ -388,3 +389,28 @@ def sys_command_file(command, filename): stdout, stderr = out.communicate() fp.close() +def import_instr_alias(alias): + ''' + Return instructions pertaining to a particular alias + + alias: (string) The alias to be imported + + ''' + + # Function to flatten nested lists + from collections import Iterable + def flatten(lis): + for item in lis: + if isinstance(item, Iterable) and not isinstance(item, str): + for x in flatten(item): + yield x + else: + yield item + + isac_path = os.path.dirname(riscv_isac.__file__) + alias_dict = load_yaml_file(isac_path + '/data/instr_alias.yaml') + if alias in alias_dict: + return list(flatten(alias_dict[alias])) + else: + return None + diff --git a/setup.cfg b/setup.cfg index e5e72ba9..509e02dd 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.14.0 +current_version = 0.15.0 commit = True tag = True diff --git a/setup.py b/setup.py index f2b0e324..aa7538ef 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read_requires(): setup( name='riscv_isac', - version='0.14.0', + version='0.15.0', description="RISC-V ISAC", long_description=readme + '\n\n', classifiers=[