From dec9db8703eb129e94cf3b1b9762c8a06d27fa6c Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 5 May 2022 10:48:16 +0530 Subject: [PATCH 01/50] cross_comb definition --- riscv_ctg/generator.py | 88 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 85 insertions(+), 3 deletions(-) diff --git a/riscv_ctg/generator.py b/riscv_ctg/generator.py index 5931ce9f..1e13713a 100644 --- a/riscv_ctg/generator.py +++ b/riscv_ctg/generator.py @@ -106,9 +106,6 @@ } ''' Dictionary mapping instruction formats to operand value variables used by those formats ''' - - - def isInt(s): ''' Utility function to check if the variable is an int type. Returns False if @@ -167,6 +164,10 @@ class Generator(): :type xl: int :type base_isa_str: str ''' + + # Supporting datastructure for cross_comb + OPS_LST = [val + [key] for key, val in OPS.items()] + def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): ''' This is a Constructor function which initializes various class variables @@ -467,6 +468,83 @@ def eval_func(cond): val_comb.append( tuple(val_tuple) ) return val_comb + def cross_comb(self, cgf): + ''' + This function finds solution for various cross-combinations defined by the coverpoints + in the CGF under the `cross_comb` node of the covergroup. + ''' + logger.debug(self.opcode + ': Generating CrossComb') + solutions = [] + + if 'cross_comb' in cgf: + cross_comb = set(cgf['cross_comb']) + else: + return + + for each in cross_comb: + parts = each.split('::') + + data = parts[0].replace(' ', '').split(':') + assgn_lst = parts[1].split(':') + cond_lst = parts[2].split(':') + + i = 0 + for i in range(len(data)): + + + isa = 'I' + if data[i] == '?': + # When instruction is not specified, + # - Gather conditions if any + # - Choose instruction based on operands in condition list + # - Generate assignments + + # Get corresponding conditions and accordingly chose instruction + problem = Problem() + for each in cond_lst[i]: + if each == '?': + pass # Choose any set of operands + else: + cond = cond_lst[i] + opr_lst = [] + + if cond.find('rd') != -1: + opr_lst.append('rd') + if cond.find('rs1') != -1: + opr_lst.append('rs1') + if cond.find('rs2') != -1: + opr_lst.append('rs2') + if cond.find('rs3') != -1: + opr_lst.append('rs3') + + # Get all possible formats based on operands in conditions + problem.addVariable('f', Generator.OPS_LST) + problem.addConstraint(lambda f: all(item in f for item in opr_lst), ('f')) + + # Get all possible formats + opr_formats = [val[-1] for key, val in problem.getSolutions().items()] + + # Choose instruction + + + # Get assignments if any and execute them + if assgn_lst[i] != '?': + assgns = assgn_lst[i].split(';') + + else: + instr = data[i] # Get the instruction + + + + + + + + + + + + def __jfmt_instr__(self,op=None,val=None): cond_str = '' if op: @@ -1319,3 +1397,7 @@ def __write_test__(self, file_name,node,label,instr_dict, op_node, usage_str): sign.append("#ifdef rvtest_gpr_save\n"+signode_template.substitute({'n':32,'label':"gpr_save"})+"\n#endif\n") with open(file_name,"w") as fd: fd.write(usage_str + test_template.safe_substitute(data='\n'.join(data),test=test,sig='\n'.join(sign),isa=op_node_isa,opcode=opcode,extension=extension,label=label)) + +if __name__ == '__main__': + a = Generator('lol', 'lol', 'lol', False, 32, 32, 'kaka') + get_it = a.cross_comb('') \ No newline at end of file From 4b3c081b3b7f3b6afbd52b51d7cb1b8c85bbce6c Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 5 May 2022 10:48:29 +0530 Subject: [PATCH 02/50] Data-structure --- riscv_ctg/ctg.py | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index a5443b51..1c4b4d17 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -1,6 +1,8 @@ # See LICENSE.incore file for details +from collections import defaultdict import os,re +import pprint import multiprocessing as mp import time @@ -13,6 +15,29 @@ from math import * from riscv_ctg.__init__ import __version__ +def gen_format_data(): + ''' + Generate dictionary from template.yaml file with the structure: + Format: + - ISA + - Mnemonics + ''' + op_template = utils.load_yaml(const.template_file) + + # Initialize nested dictionary + nested_dict = lambda: defaultdict(nested_dict) + format_dict = nested_dict() + + for mnemonic, data in op_template.items(): + if mnemonic not in ['metadata']: + format_type = data['formattype'] + isa = data['isa'] + + for each in isa: + format_dict[format_type][each][mnemonic] = None + + return format_dict + def create_test(usage_str, node,label,base_isa,max_inst): global op_template global ramdomize @@ -94,3 +119,6 @@ def ctg(verbose, out, random ,xlen_arg, cgf_file,num_procs,base_isa, max_inst): results = pool.starmap(create_test, [(usage_str, node,label,base_isa,max_inst) for label,node in cgf.items()]) pool.close() +if __name__ == '__main__': + gen_format_data() + print(locals()) \ No newline at end of file From 64e432020ab4cb357cda061e97fc635df3bab4fd Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 5 May 2022 23:07:44 +0530 Subject: [PATCH 03/50] Supporting Datastructures for cross_comb --- riscv_ctg/utils.py | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/riscv_ctg/utils.py b/riscv_ctg/utils.py index 10949799..5ff8f932 100644 --- a/riscv_ctg/utils.py +++ b/riscv_ctg/utils.py @@ -8,6 +8,8 @@ from riscv_ctg.log import logger import ruamel from ruamel.yaml import YAML +from collections import defaultdict +import riscv_ctg.constants as const yaml = YAML(typ="rt") yaml.default_flow_style = False @@ -23,6 +25,40 @@ def load_yaml(foo): logger.error(error) raise SystemExit +def gen_format_data(): + ''' + Generate dictionary from template.yaml file with the structure: + Format: + - ISA + - Mnemonics + ''' + op_template = load_yaml(const.template_file) + + # Initialize nested dictionary + nested_dict = lambda: defaultdict(nested_dict) + format_dict = nested_dict() + + for mnemonic, data in op_template.items(): + if mnemonic not in ['metadata']: + format_type = data['formattype'] + isa = data['isa'] + + for each in isa: + format_dict[format_type][each][mnemonic] = None + + return format_dict + +def get_instr_list(): + ''' + Get list of all instructions defined in template file + ''' + op_template = load_yaml(const.template_file) + + instr_lst = list(op_template.keys()) + instr_lst.remove('metadata') + + return instr_lst + class makeUtil(): """ Utility for ease of use of make commands like `make` and `pmake`. From ca72105a6dc58bbb6b37bee7dd3cce09ae741d14 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 5 May 2022 23:08:00 +0530 Subject: [PATCH 04/50] Moved supporting datastructures for cross_comb --- riscv_ctg/ctg.py | 25 ------------------------- 1 file changed, 25 deletions(-) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index 1c4b4d17..0fe19584 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -1,8 +1,6 @@ # See LICENSE.incore file for details -from collections import defaultdict import os,re -import pprint import multiprocessing as mp import time @@ -15,29 +13,6 @@ from math import * from riscv_ctg.__init__ import __version__ -def gen_format_data(): - ''' - Generate dictionary from template.yaml file with the structure: - Format: - - ISA - - Mnemonics - ''' - op_template = utils.load_yaml(const.template_file) - - # Initialize nested dictionary - nested_dict = lambda: defaultdict(nested_dict) - format_dict = nested_dict() - - for mnemonic, data in op_template.items(): - if mnemonic not in ['metadata']: - format_type = data['formattype'] - isa = data['isa'] - - for each in isa: - format_dict[format_type][each][mnemonic] = None - - return format_dict - def create_test(usage_str, node,label,base_isa,max_inst): global op_template global ramdomize From 35e097b676fa9249d1615cbab289f73ecca39d05 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 5 May 2022 23:08:17 +0530 Subject: [PATCH 05/50] Instruction selection CSP --- riscv_ctg/generator.py | 110 +++++++++++++++++++++++++++-------------- 1 file changed, 73 insertions(+), 37 deletions(-) diff --git a/riscv_ctg/generator.py b/riscv_ctg/generator.py index 1e13713a..dc08943c 100644 --- a/riscv_ctg/generator.py +++ b/riscv_ctg/generator.py @@ -3,8 +3,12 @@ from collections import defaultdict from constraint import * import re + from riscv_ctg.constants import * from riscv_ctg.log import logger +import riscv_ctg.utils as utils +import riscv_ctg.constants as const + import time from math import * import struct @@ -164,9 +168,13 @@ class Generator(): :type xl: int :type base_isa_str: str ''' - - # Supporting datastructure for cross_comb - OPS_LST = [val + [key] for key, val in OPS.items()] + + # Template dictionary + OP_TEMPLATE = utils.load_yaml(const.template_file) + + # Supporting data-structure for cross_comb + FORMAT_DICT = utils.gen_format_data() + INSTR_LST = utils.get_instr_list() def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): ''' @@ -473,8 +481,8 @@ def cross_comb(self, cgf): This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. ''' - logger.debug(self.opcode + ': Generating CrossComb') - solutions = [] + #logger.debug(self.opcode + ': Generating CrossComb') + #solutions = [] if 'cross_comb' in cgf: cross_comb = set(cgf['cross_comb']) @@ -484,15 +492,15 @@ def cross_comb(self, cgf): for each in cross_comb: parts = each.split('::') - data = parts[0].replace(' ', '').split(':') - assgn_lst = parts[1].split(':') - cond_lst = parts[2].split(':') + data = parts[0].replace(' ', '')[1:-1].split(':') + assgn_lst = parts[1].replace(' ', '')[1:-1].split(':') + cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') i = 0 + isa = 'I' + + problem = Problem() for i in range(len(data)): - - - isa = 'I' if data[i] == '?': # When instruction is not specified, # - Gather conditions if any @@ -500,39 +508,66 @@ def cross_comb(self, cgf): # - Generate assignments # Get corresponding conditions and accordingly chose instruction - problem = Problem() - for each in cond_lst[i]: - if each == '?': - pass # Choose any set of operands - else: - cond = cond_lst[i] - opr_lst = [] - - if cond.find('rd') != -1: - opr_lst.append('rd') - if cond.find('rs1') != -1: - opr_lst.append('rs1') - if cond.find('rs2') != -1: - opr_lst.append('rs2') - if cond.find('rs3') != -1: - opr_lst.append('rs3') - - # Get all possible formats based on operands in conditions - problem.addVariable('f', Generator.OPS_LST) - problem.addConstraint(lambda f: all(item in f for item in opr_lst), ('f')) - - # Get all possible formats - opr_formats = [val[-1] for key, val in problem.getSolutions().items()] + cond = cond_lst[i] + if cond.find('?') != -1: + + # Get possible instructions from extension of last instruction defined in coverpoint + problem.reset() + problem.addVariable('i', Generator.INSTR_LST) + problem.addConstraint(lambda i: isa in Generator.OP_TEMPLATE[i]['isa']) + instrs_sol = problem.getSolutions() + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + + else: + opr_lst = [] + + if cond.find('rd') != -1: + opr_lst.append('rd') + if cond.find('rs1') != -1: + opr_lst.append('rs1') + if cond.find('rs2') != -1: + opr_lst.append('rs2') + if cond.find('rs3') != -1: + opr_lst.append('rs3') + + # Get all possible formats based on operands in conditions + problem.reset() + problem.addVariable('f', OPS) + problem.addConstraint(lambda f: all(item in OPS[f] for item in opr_lst)) + + opr_formats = problem.getSolutions() + opr_formats = [list(each.items())[0][1] for each in opr_formats] + + # Get possible instructions + problem.reset() + problem.addVariable('i', Generator.INSTR_LST) + problem.addConstraint(lambda i: isa in Generator.OP_TEMPLATE[i]['isa'] and Generator.OP_TEMPLATE[i]['formattype'] in opr_formats) + + instrs_sol = problem.getSolutions() + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] # Choose instruction + instr = instrs_sol[0] # For now + print(instrs_sol) + + # Choose operand values + formattype = Generator.OP_TEMPLATE[instr]['formattype'] + oprs = OPS[formattype] + problem.reset() + for var in oprs: + problem.addVariable(var, list(self.datasets[var])) - # Get assignments if any and execute them if assgn_lst[i] != '?': assgns = assgn_lst[i].split(';') + for each in assgns: + exec(each) + print(locals) + else: instr = data[i] # Get the instruction + isa = Generator.OP_TEMPLATE[instr]['isa'][0] @@ -1399,5 +1434,6 @@ def __write_test__(self, file_name,node,label,instr_dict, op_node, usage_str): fd.write(usage_str + test_template.safe_substitute(data='\n'.join(data),test=test,sig='\n'.join(sign),isa=op_node_isa,opcode=opcode,extension=extension,label=label)) if __name__ == '__main__': - a = Generator('lol', 'lol', 'lol', False, 32, 32, 'kaka') - get_it = a.cross_comb('') \ No newline at end of file + + cross = {'cross_comb' : {'[add : ? : mul : ? : ? : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a] ' : 0}} + get_it = Generator.cross_comb(cross) \ No newline at end of file From b0d8b084a6d30b1f9d6611481516626ce0555353 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Fri, 13 May 2022 10:57:22 +0530 Subject: [PATCH 06/50] Choosing instructions from alias yaml --- riscv_ctg/generator.py | 127 +++++++++++++++++++++++++++++------------ 1 file changed, 90 insertions(+), 37 deletions(-) diff --git a/riscv_ctg/generator.py b/riscv_ctg/generator.py index dc08943c..a56ccc22 100644 --- a/riscv_ctg/generator.py +++ b/riscv_ctg/generator.py @@ -4,6 +4,7 @@ from constraint import * import re +import riscv_isac.utils as isac_utils from riscv_ctg.constants import * from riscv_ctg.log import logger import riscv_ctg.utils as utils @@ -476,7 +477,7 @@ def eval_func(cond): val_comb.append( tuple(val_tuple) ) return val_comb - def cross_comb(self, cgf): + def cross_comb(cgf, xlen): ''' This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. @@ -489,6 +490,22 @@ def cross_comb(self, cgf): else: return + dntcare_instrs = isac_utils.import_instr_alias('rv' + str(xlen) + 'i_arith') + isac_utils.import_instr_alias('rv' + str(xlen) + 'i_shift') + + # This function retrieves available operands in a string + def get_oprs(opr_str): + opr_lst = [] + if opr_str.find('rd') != -1: + opr_lst.append('rd') + if opr_str.find('rs1') != -1: + opr_lst.append('rs1') + if opr_str.find('rs2') != -1: + opr_lst.append('rs2') + if opr_str.find('rs3') != -1: + opr_lst.append('rs3') + + return opr_lst + for each in cross_comb: parts = each.split('::') @@ -496,9 +513,7 @@ def cross_comb(self, cgf): assgn_lst = parts[1].replace(' ', '')[1:-1].split(':') cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') - i = 0 - isa = 'I' - + i = 0 problem = Problem() for i in range(len(data)): if data[i] == '?': @@ -511,63 +526,101 @@ def cross_comb(self, cgf): cond = cond_lst[i] if cond.find('?') != -1: - # Get possible instructions from extension of last instruction defined in coverpoint + # Check variables in assignment list and generate required operand list + assgn = assgn_lst[i] + opr_lst = get_oprs(assgn) + + # Get possible instructions based on the operand list problem.reset() - problem.addVariable('i', Generator.INSTR_LST) - problem.addConstraint(lambda i: isa in Generator.OP_TEMPLATE[i]['isa']) + problem.addVariable('i', dntcare_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() - instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + else: - opr_lst = [] - - if cond.find('rd') != -1: - opr_lst.append('rd') - if cond.find('rs1') != -1: - opr_lst.append('rs1') - if cond.find('rs2') != -1: - opr_lst.append('rs2') - if cond.find('rs3') != -1: - opr_lst.append('rs3') - - # Get all possible formats based on operands in conditions - problem.reset() - problem.addVariable('f', OPS) - problem.addConstraint(lambda f: all(item in OPS[f] for item in opr_lst)) - opr_formats = problem.getSolutions() - opr_formats = [list(each.items())[0][1] for each in opr_formats] + opr_lst = [] + # Extract required operands from condition list + opr_lst = get_oprs(cond) + + # Extract required operands from assignment list + assgn = assgn_lst[i] + opr_lst += get_oprs(assgn) + + # Remove redundant operands + opr_lst = list(set(opr_lst)) + # Get possible instructions problem.reset() - problem.addVariable('i', Generator.INSTR_LST) - problem.addConstraint(lambda i: isa in Generator.OP_TEMPLATE[i]['isa'] and Generator.OP_TEMPLATE[i]['formattype'] in opr_formats) - + problem.addVariable('i', dntcare_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] # Choose instruction - instr = instrs_sol[0] # For now print(instrs_sol) + instr = instrs_sol[0] # For now + # Choose operand values formattype = Generator.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] - problem.reset() - for var in oprs: - problem.addVariable(var, list(self.datasets[var])) + problem.reset() + problem.addVariables(oprs, list(range(32))) + + # Add contraint if any + + ''' # Get assignments if any and execute them if assgn_lst[i] != '?': assgns = assgn_lst[i].split(';') for each in assgns: exec(each) print(locals) - +''' else: - instr = data[i] # Get the instruction - isa = Generator.OP_TEMPLATE[instr]['isa'][0] + instr = data[i] # Get the instruction/alias + cond = cond_lst[i] + assgn = assgn_lst[i] + + if instr in Generator.OP_TEMPLATE: + formattype = Generator.OP_TEMPLATE[instr]['formattype'] + oprs = OPS[formattype] + else: + alias_instrs = isac_utils.import_instr_alias(instr) + if alias_instrs: + problem.reset() + problem.addVariable('i', alias_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + instrs_sol = problem.getSolutions() + + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + + print(instrs_sol) + + # Select and instruction + + # Assign values to operands + if cond.find('?') != -1: + problem.reset() + problem.addVariables(oprs, list(range(32))) + + else: + problem.reset() + problem.addVariables(oprs, list(range(32))) + + # Add constraint + + # Get operand values + + # Execute assignments + + @@ -1435,5 +1488,5 @@ def __write_test__(self, file_name,node,label,instr_dict, op_node, usage_str): if __name__ == '__main__': - cross = {'cross_comb' : {'[add : ? : mul : ? : ? : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a] ' : 0}} - get_it = Generator.cross_comb(cross) \ No newline at end of file + cross = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + get_it = Generator.cross_comb(cross, 32) \ No newline at end of file From fe0d15156f043ab8e330e515ca82daaee0a147fb Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Sun, 15 May 2022 22:13:25 +0530 Subject: [PATCH 07/50] Restore to earlier version --- riscv_ctg/generator.py | 185 ++--------------------------------------- 1 file changed, 7 insertions(+), 178 deletions(-) diff --git a/riscv_ctg/generator.py b/riscv_ctg/generator.py index a56ccc22..229e63fc 100644 --- a/riscv_ctg/generator.py +++ b/riscv_ctg/generator.py @@ -3,13 +3,8 @@ from collections import defaultdict from constraint import * import re - -import riscv_isac.utils as isac_utils from riscv_ctg.constants import * from riscv_ctg.log import logger -import riscv_ctg.utils as utils -import riscv_ctg.constants as const - import time from math import * import struct @@ -111,6 +106,9 @@ } ''' Dictionary mapping instruction formats to operand value variables used by those formats ''' + + + def isInt(s): ''' Utility function to check if the variable is an int type. Returns False if @@ -169,14 +167,6 @@ class Generator(): :type xl: int :type base_isa_str: str ''' - - # Template dictionary - OP_TEMPLATE = utils.load_yaml(const.template_file) - - # Supporting data-structure for cross_comb - FORMAT_DICT = utils.gen_format_data() - INSTR_LST = utils.get_instr_list() - def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): ''' This is a Constructor function which initializes various class variables @@ -477,162 +467,6 @@ def eval_func(cond): val_comb.append( tuple(val_tuple) ) return val_comb - def cross_comb(cgf, xlen): - ''' - This function finds solution for various cross-combinations defined by the coverpoints - in the CGF under the `cross_comb` node of the covergroup. - ''' - #logger.debug(self.opcode + ': Generating CrossComb') - #solutions = [] - - if 'cross_comb' in cgf: - cross_comb = set(cgf['cross_comb']) - else: - return - - dntcare_instrs = isac_utils.import_instr_alias('rv' + str(xlen) + 'i_arith') + isac_utils.import_instr_alias('rv' + str(xlen) + 'i_shift') - - # This function retrieves available operands in a string - def get_oprs(opr_str): - opr_lst = [] - if opr_str.find('rd') != -1: - opr_lst.append('rd') - if opr_str.find('rs1') != -1: - opr_lst.append('rs1') - if opr_str.find('rs2') != -1: - opr_lst.append('rs2') - if opr_str.find('rs3') != -1: - opr_lst.append('rs3') - - return opr_lst - - for each in cross_comb: - parts = each.split('::') - - data = parts[0].replace(' ', '')[1:-1].split(':') - assgn_lst = parts[1].replace(' ', '')[1:-1].split(':') - cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') - - i = 0 - problem = Problem() - for i in range(len(data)): - if data[i] == '?': - # When instruction is not specified, - # - Gather conditions if any - # - Choose instruction based on operands in condition list - # - Generate assignments - - # Get corresponding conditions and accordingly chose instruction - cond = cond_lst[i] - if cond.find('?') != -1: - - # Check variables in assignment list and generate required operand list - assgn = assgn_lst[i] - opr_lst = get_oprs(assgn) - - # Get possible instructions based on the operand list - problem.reset() - problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) - instrs_sol = problem.getSolutions() - - instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - - else: - - opr_lst = [] - - # Extract required operands from condition list - opr_lst = get_oprs(cond) - - # Extract required operands from assignment list - assgn = assgn_lst[i] - opr_lst += get_oprs(assgn) - - # Remove redundant operands - opr_lst = list(set(opr_lst)) - - # Get possible instructions - problem.reset() - problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) - instrs_sol = problem.getSolutions() - - instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - - # Choose instruction - print(instrs_sol) - - instr = instrs_sol[0] # For now - - # Choose operand values - formattype = Generator.OP_TEMPLATE[instr]['formattype'] - oprs = OPS[formattype] - - problem.reset() - problem.addVariables(oprs, list(range(32))) - - # Add contraint if any - - ''' - # Get assignments if any and execute them - if assgn_lst[i] != '?': - assgns = assgn_lst[i].split(';') - for each in assgns: - exec(each) - print(locals) -''' - - else: - instr = data[i] # Get the instruction/alias - cond = cond_lst[i] - assgn = assgn_lst[i] - - if instr in Generator.OP_TEMPLATE: - formattype = Generator.OP_TEMPLATE[instr]['formattype'] - oprs = OPS[formattype] - else: - alias_instrs = isac_utils.import_instr_alias(instr) - if alias_instrs: - problem.reset() - problem.addVariable('i', alias_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) - instrs_sol = problem.getSolutions() - - instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - - print(instrs_sol) - - # Select and instruction - - # Assign values to operands - if cond.find('?') != -1: - problem.reset() - problem.addVariables(oprs, list(range(32))) - - else: - problem.reset() - problem.addVariables(oprs, list(range(32))) - - # Add constraint - - # Get operand values - - # Execute assignments - - - - - - - - - - - - - - def __jfmt_instr__(self,op=None,val=None): cond_str = '' if op: @@ -1063,7 +897,7 @@ def eval_inst_coverage(coverpoints,instr): for instr in instr_dict: unique = False skip_val = False - if instr['inst'] in cgf['opcode']: + if instr['inst'] in cgf['mnemonics']: if 'rs1' in instr and 'rs2' in instr: if instr['rs1'] == instr['rs2']: skip_val = True @@ -1089,7 +923,7 @@ def eval_inst_coverage(coverpoints,instr): else: i+=1 - if 'IP' in self.opnode['isa']: + if any('IP' in isa for isa in self.opnode['isa']): if 'p64_profile' in self.opnode: gen_pair_reg_data(final_instr, xlen, self.opnode['bit_width'], self.opnode['p64_profile']) elif 'bit_width' in self.opnode: @@ -1330,7 +1164,7 @@ def reformat_instr(self, instr_dict): :type instr_dict: list :return: list of dictionaries containing the various values necessary for the macro ''' - if 'IP' in self.opnode['isa']: + if any('IP' in isa for isa in self.opnode['isa']): # instr_dict is already in the desired format for instructions that perform SIMD operations, or Zpsfoperand instructions in RV32. if 'bit_width' in self.opnode or (xlen == 32 and 'p64_profile' in self.opnode): return instr_dict @@ -1413,7 +1247,7 @@ def __write_test__(self, file_name,node,label,instr_dict, op_node, usage_str): data.append("test_fp:") code.append("RVTEST_FP_ENABLE()") - if 'IP' in self.opnode['isa']: + if any('IP' in isa for isa in self.opnode['isa']): code.append("RVTEST_VXSAT_ENABLE()") if xlen == 32 and 'p64_profile' in self.opnode: @@ -1485,8 +1319,3 @@ def __write_test__(self, file_name,node,label,instr_dict, op_node, usage_str): sign.append("#ifdef rvtest_gpr_save\n"+signode_template.substitute({'n':32,'label':"gpr_save"})+"\n#endif\n") with open(file_name,"w") as fd: fd.write(usage_str + test_template.safe_substitute(data='\n'.join(data),test=test,sig='\n'.join(sign),isa=op_node_isa,opcode=opcode,extension=extension,label=label)) - -if __name__ == '__main__': - - cross = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} - get_it = Generator.cross_comb(cross, 32) \ No newline at end of file From 2fbe8d88ce90ab0894ad71e73e6208ffe4af685e Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Sun, 15 May 2022 22:14:02 +0530 Subject: [PATCH 08/50] Move cross_comb to separate file --- riscv_ctg/cross_comb.py | 381 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 381 insertions(+) create mode 100644 riscv_ctg/cross_comb.py diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py new file mode 100644 index 00000000..351c1b04 --- /dev/null +++ b/riscv_ctg/cross_comb.py @@ -0,0 +1,381 @@ +# See LICENSE.incore for details +import random +from collections import defaultdict +from constraint import * +import re + +import riscv_isac.utils as isac_utils +from riscv_ctg.constants import * +from riscv_ctg.log import logger +import riscv_ctg.utils as utils +import riscv_ctg.constants as const + +import time +from math import * +import struct +import sys +import itertools + +one_operand_finstructions = ["fsqrt.s","fmv.x.w","fcvt.wu.s","fcvt.w.s","fclass.s","fcvt.l.s","fcvt.lu.s","fcvt.s.l","fcvt.s.lu"] +two_operand_finstructions = ["fadd.s","fsub.s","fmul.s","fdiv.s","fmax.s","fmin.s","feq.s","flt.s","fle.s","fsgnj.s","fsgnjn.s","fsgnjx.s"] +three_operand_finstructions = ["fmadd.s","fmsub.s","fnmadd.s","fnmsub.s"] + +one_operand_dinstructions = ["fsqrt.d","fclass.d","fcvt.w.d","fcvt.wu.d","fcvt.d.w","fcvt.d.wu"] +two_operand_dinstructions = ["fadd.d","fsub.d","fmul.d","fdiv.d","fmax.d","fmin.d","feq.d","flt.d","fle.d","fsgnj.d","fsgnjn.d","fsgnjx.d"] +three_operand_dinstructions = ["fmadd.d","fmsub.d","fnmadd.d","fnmsub.d"] +from riscv_ctg.dsp_function import * + +twos_xlen = lambda x: twos(x,xlen) + +OPS = { + 'rformat': ['rs1', 'rs2', 'rd'], + 'iformat': ['rs1', 'rd'], + 'sformat': ['rs1', 'rs2'], + 'bsformat': ['rs1', 'rs2', 'rd'], + 'bformat': ['rs1', 'rs2'], + 'uformat': ['rd'], + 'jformat': ['rd'], + 'crformat': ['rs1', 'rs2'], + 'cmvformat': ['rd', 'rs2'], + 'ciformat': ['rd'], + 'cssformat': ['rs2'], + 'ciwformat': ['rd'], + 'clformat': ['rd', 'rs1'], + 'csformat': ['rs1', 'rs2'], + 'caformat': ['rs1', 'rs2'], + 'cbformat': ['rs1'], + 'cjformat': [], + 'kformat': ['rs1','rd'], + 'frformat': ['rs1', 'rs2', 'rd'], + 'fsrformat': ['rs1', 'rd'], + 'fr4format': ['rs1', 'rs2', 'rs3', 'rd'], + 'pbrrformat': ['rs1', 'rs2', 'rd'], + 'phrrformat': ['rs1', 'rs2', 'rd'], + 'pbrformat': ['rs1', 'rd'], + 'phrformat': ['rs1', 'rd'], + 'pbriformat': ['rs1', 'rd'], + 'phriformat': ['rs1', 'rd'], + 'psbrrformat': ['rs1', 'rs2', 'rd'], + 'pshrrformat': ['rs1', 'rs2', 'rd'], + 'pwrrformat': ['rs1', 'rs2', 'rd'], + 'pwriformat': ['rs1', 'rd'], + 'pwrformat': ['rs1', 'rd'], + 'pswrrformat': ['rs1', 'rs2', 'rd'], + 'pwhrrformat': ['rs1', 'rs2', 'rd'], + 'pphrrformat': ['rs1', 'rs2', 'rd'], + 'ppbrrformat': ['rs1', 'rs2', 'rd'], + 'prrformat': ['rs1', 'rs2', 'rd'], + 'prrrformat': ['rs1', 'rs2', 'rs3', 'rd'] +} +''' Dictionary mapping instruction formats to operands used by those formats ''' + +VALS = { + 'rformat': "['rs1_val', 'rs2_val']", + 'iformat': "['rs1_val', 'imm_val']", + 'sformat': "['rs1_val', 'rs2_val', 'imm_val']", + 'bsformat': "['rs1_val', 'rs2_val', 'imm_val']", + 'bformat': "['rs1_val', 'rs2_val', 'imm_val']", + 'uformat': "['imm_val']", + 'jformat': "['imm_val']", + 'crformat': "['rs1_val', 'rs2_val']", + 'cmvformat': "['rs2_val']", + 'ciformat': "['rs1_val', 'imm_val']", + 'cssformat': "['rs2_val', 'imm_val']", + 'ciwformat': "['imm_val']", + 'clformat': "['rs1_val', 'imm_val']", + 'csformat': "['rs1_val', 'rs2_val', 'imm_val']", + 'caformat': "['rs1_val', 'rs2_val']", + 'cbformat': "['rs1_val', 'imm_val']", + 'cjformat': "['imm_val']", + 'kformat': "['rs1_val']", + 'frformat': "['rs1_val', 'rs2_val', 'rm_val']", + 'fsrformat': "['rs1_val', 'rm_val']", + 'fr4format': "['rs1_val', 'rs2_val', 'rs3_val', 'rm_val']", + 'pbrrformat': 'simd_val_vars("rs1", xlen, 8) + simd_val_vars("rs2", xlen, 8)', + 'phrrformat': 'simd_val_vars("rs1", xlen, 16) + simd_val_vars("rs2", xlen, 16)', + 'pbrformat': 'simd_val_vars("rs1", xlen, 8)', + 'phrformat': 'simd_val_vars("rs1", xlen, 16)', + 'pbriformat': 'simd_val_vars("rs1", xlen, 8) + ["imm_val"]', + 'phriformat': 'simd_val_vars("rs1", xlen, 16) + ["imm_val"]', + 'psbrrformat': 'simd_val_vars("rs1", xlen, 8) + ["rs2_val"]', + 'pshrrformat': 'simd_val_vars("rs1", xlen, 16) + ["rs2_val"]', + 'pwrrformat': 'simd_val_vars("rs1", xlen, 32) + simd_val_vars("rs2", xlen, 32)', + 'pwriformat': 'simd_val_vars("rs1", xlen, 32) + ["imm_val"]', + 'pwrformat': 'simd_val_vars("rs1", xlen, 32)', + 'pswrrformat': 'simd_val_vars("rs1", xlen, 32) + ["rs2_val"]', + 'pwhrrformat': 'simd_val_vars("rs1", xlen, 32) + simd_val_vars("rs2", xlen, 16)', + 'pphrrformat': '["rs1_val"] + simd_val_vars("rs2", xlen, 16)', + 'ppbrrformat': '["rs1_val"] + simd_val_vars("rs2", xlen, 8)', + 'prrformat': '["rs1_val", "rs2_val"]', + 'prrrformat': "['rs1_val', 'rs2_val' , 'rs3_val']" +} +''' Dictionary mapping instruction formats to operand value variables used by those formats ''' + +def isInt(s): + ''' + Utility function to check if the variable is an int type. Returns False if + not. + ''' + try: + int(s) + return True + except ValueError: + return False + +def get_default_registers(ops, datasets): + problem = Problem() + not_x0 = lambda x: x not in ['x0'] + + for op in ops: + dataset = datasets[op] + # problem.addVariable(op,list(random.sample(dataset, len(dataset)))) + problem.addVariable(op,dataset) + problem.addConstraint(not_x0,tuple([op])) + if len(ops) > 1: + cond = " and ".join(["!=".join(x) for x in itertools.combinations(ops,2) if x[0]!=x[1]]) + else: + cond = 'True' + def unique_constraint(*args): + for var,val in zip(ops,args): + locals()[var] = val + return eval(cond) + problem.addConstraint(unique_constraint,tuple(ops)) + solution = None + count = 0 + while solution is None and count < 5: + solution = problem.getSolution() + count += 1 + if count == 5: + return [] + else: + return solution + +class Generator(): + ''' + A generator class to generate RISC-V assembly tests for a given instruction + format, opcode and a set of coverpoints. + + :param fmt: the RISC-V instruction format type to be used for the test generation. + :param opnode: dictionary node from the attributes YAML that is to be used in the test generation. + :param opcode: name of the instruction opcode. + :param randomization: a boolean variable indicating if the random constraint solvers must be employed. + :param xl: an integer indicating the XLEN value to be used. + :param base_isa_str: The base isa to be used for the tests. One of [rv32e,rv32i,rv64i] + + :type fmt: str + :type opnode: dict + :type opcode: str + :type randomization: bool + :type xl: int + :type base_isa_str: str + ''' + + # Template dictionary + OP_TEMPLATE = utils.load_yaml(const.template_file) + + # Supporting data-structure for cross_comb + FORMAT_DICT = utils.gen_format_data() + INSTR_LST = utils.get_instr_list() + + def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): + ''' + This is a Constructor function which initializes various class variables + depending on the arguments. + + The function also creates a dictionary of datasets for each operand. The + dictionary basically indicates what registers from the register file are to be used + when generating solutions for coverpoints. The datasets are limited to + to reduce the time taken by solvers to arrive at a solution. + + A similar dictionary is created for the values to be used by the operand + registers. + + ''' + global xlen + global flen + global base_isa + xlen = xl + flen = fl + base_isa = base_isa_str + self.fmt = fmt + self.opcode = opcode + + self.op_vars = OPS[fmt] + + self.val_vars = eval(VALS[fmt]) + + if opcode in ['sw', 'sh', 'sb', 'lw', 'lhu', 'lh', 'lb', 'lbu', 'ld', 'lwu', 'sd',"jal","beq","bge","bgeu","blt","bltu","bne","jalr","flw","fsw","fld","fsd"]: + self.val_vars = self.val_vars + ['ea_align'] + self.template = opnode['template'] + self.opnode = opnode + self.stride = opnode['stride'] + if 'operation' in opnode: + self.operation = opnode['operation'] + else: + self.operation = None + datasets = {} + i=10 + for entry in self.op_vars: + key = entry+"_op_data" + if key in opnode: + datasets[entry] = eval(opnode[key]) + else: + datasets[entry] = ['x'+str(i)] + i+=1 + for entry in self.val_vars: + key = entry+"_data" + if key in opnode: + datasets[entry] = eval(opnode[key]) + else: + datasets[entry] = [0] + self.datasets = datasets + self.random=randomization + self.default_regs = get_default_registers(self.op_vars, self.datasets) + + def cross_comb(cgf, xlen): + ''' + This function finds solution for various cross-combinations defined by the coverpoints + in the CGF under the `cross_comb` node of the covergroup. + ''' + #logger.debug(self.opcode + ': Generating CrossComb') + #solutions = [] + + if 'cross_comb' in cgf: + cross_comb = set(cgf['cross_comb']) + else: + return + + dntcare_instrs = isac_utils.import_instr_alias('rv' + str(xlen) + 'i_arith') + isac_utils.import_instr_alias('rv' + str(xlen) + 'i_shift') + + # This function retrieves available operands in a string + def get_oprs(opr_str): + opr_lst = [] + if opr_str.find('rd') != -1: + opr_lst.append('rd') + if opr_str.find('rs1') != -1: + opr_lst.append('rs1') + if opr_str.find('rs2') != -1: + opr_lst.append('rs2') + if opr_str.find('rs3') != -1: + opr_lst.append('rs3') + + return opr_lst + + for each in cross_comb: + parts = each.split('::') + + data = parts[0].replace(' ', '')[1:-1].split(':') + assgn_lst = parts[1].replace(' ', '')[1:-1].split(':') + cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') + + i = 0 + problem = Problem() + for i in range(len(data)): + if data[i] == '?': + # When instruction is not specified, + # - Gather conditions if any + # - Choose instruction based on operands in condition list + # - Generate assignments + + # Get corresponding conditions and accordingly chose instruction + cond = cond_lst[i] + if cond.find('?') != -1: + + # Check variables in assignment list and generate required operand list + assgn = assgn_lst[i] + opr_lst = get_oprs(assgn) + + # Get possible instructions based on the operand list + problem.reset() + problem.addVariable('i', dntcare_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + instrs_sol = problem.getSolutions() + + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + + else: + + opr_lst = [] + + # Extract required operands from condition list + opr_lst = get_oprs(cond) + + # Extract required operands from assignment list + assgn = assgn_lst[i] + opr_lst += get_oprs(assgn) + + # Remove redundant operands + opr_lst = list(set(opr_lst)) + + # Get possible instructions + problem.reset() + problem.addVariable('i', dntcare_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + instrs_sol = problem.getSolutions() + + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + + # Choose instruction + print(instrs_sol) + + instr = instrs_sol[0] # For now + + # Choose operand values + formattype = Generator.OP_TEMPLATE[instr]['formattype'] + oprs = OPS[formattype] + + problem.reset() + problem.addVariables(oprs, list(range(32))) + + # Add contraint if any + + ''' + # Get assignments if any and execute them + if assgn_lst[i] != '?': + assgns = assgn_lst[i].split(';') + for each in assgns: + exec(each) + print(locals) +''' + + else: + instr = data[i] # Get the instruction/alias + cond = cond_lst[i] + assgn = assgn_lst[i] + + if instr in Generator.OP_TEMPLATE: + formattype = Generator.OP_TEMPLATE[instr]['formattype'] + oprs = OPS[formattype] + else: + alias_instrs = isac_utils.import_instr_alias(instr) + if alias_instrs: + problem.reset() + problem.addVariable('i', alias_instrs) + problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + instrs_sol = problem.getSolutions() + + instrs_sol = [list(each.items())[0][1] for each in instrs_sol] + + print(instrs_sol) + + # Select and instruction + + # Assign values to operands + if cond.find('?') != -1: + problem.reset() + problem.addVariables(oprs, list(range(32))) + + else: + problem.reset() + problem.addVariables(oprs, list(range(32))) + + # Add constraint + + # Get operand values + + # Execute assignments + +if __name__ == '__main__': + + cross = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + get_it = Generator.cross_comb(cross, 32) \ No newline at end of file From 30d9cc486094b48975ad7333a545471a04b11ee3 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Sun, 15 May 2022 22:18:39 +0530 Subject: [PATCH 09/50] Cleanup --- riscv_ctg/cross_comb.py | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 351c1b04..1985fa71 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -16,13 +16,6 @@ import sys import itertools -one_operand_finstructions = ["fsqrt.s","fmv.x.w","fcvt.wu.s","fcvt.w.s","fclass.s","fcvt.l.s","fcvt.lu.s","fcvt.s.l","fcvt.s.lu"] -two_operand_finstructions = ["fadd.s","fsub.s","fmul.s","fdiv.s","fmax.s","fmin.s","feq.s","flt.s","fle.s","fsgnj.s","fsgnjn.s","fsgnjx.s"] -three_operand_finstructions = ["fmadd.s","fmsub.s","fnmadd.s","fnmsub.s"] - -one_operand_dinstructions = ["fsqrt.d","fclass.d","fcvt.w.d","fcvt.wu.d","fcvt.d.w","fcvt.d.wu"] -two_operand_dinstructions = ["fadd.d","fsub.d","fmul.d","fdiv.d","fmax.d","fmin.d","feq.d","flt.d","fle.d","fsgnj.d","fsgnjn.d","fsgnjx.d"] -three_operand_dinstructions = ["fmadd.d","fmsub.d","fnmadd.d","fnmsub.d"] from riscv_ctg.dsp_function import * twos_xlen = lambda x: twos(x,xlen) @@ -150,7 +143,7 @@ def unique_constraint(*args): else: return solution -class Generator(): +class cross(): ''' A generator class to generate RISC-V assembly tests for a given instruction format, opcode and a set of coverpoints. @@ -288,7 +281,7 @@ def get_oprs(opr_str): # Get possible instructions based on the operand list problem.reset() problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() instrs_sol = [list(each.items())[0][1] for each in instrs_sol] @@ -310,7 +303,7 @@ def get_oprs(opr_str): # Get possible instructions problem.reset() problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() instrs_sol = [list(each.items())[0][1] for each in instrs_sol] @@ -321,7 +314,7 @@ def get_oprs(opr_str): instr = instrs_sol[0] # For now # Choose operand values - formattype = Generator.OP_TEMPLATE[instr]['formattype'] + formattype = cross.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] problem.reset() @@ -343,15 +336,15 @@ def get_oprs(opr_str): cond = cond_lst[i] assgn = assgn_lst[i] - if instr in Generator.OP_TEMPLATE: - formattype = Generator.OP_TEMPLATE[instr]['formattype'] + if instr in cross.OP_TEMPLATE: + formattype = cross.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] else: alias_instrs = isac_utils.import_instr_alias(instr) if alias_instrs: problem.reset() problem.addVariable('i', alias_instrs) - problem.addConstraint(lambda i: all(item in OPS[Generator.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() instrs_sol = [list(each.items())[0][1] for each in instrs_sol] @@ -377,5 +370,5 @@ def get_oprs(opr_str): if __name__ == '__main__': - cross = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} - get_it = Generator.cross_comb(cross, 32) \ No newline at end of file + cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + get_it = cross.cross_comb(cross_cov, 32) \ No newline at end of file From e4be67efc7a53cf58dbb65b4ba4b88579356ae20 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 18 May 2022 00:37:44 +0530 Subject: [PATCH 10/50] Generate operand values --- riscv_ctg/cross_comb.py | 105 +++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 55 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 1985fa71..cfee2cfe 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -170,7 +170,7 @@ class cross(): FORMAT_DICT = utils.gen_format_data() INSTR_LST = utils.get_instr_list() - def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): + def __init__(self, base_isa_str): ''' This is a Constructor function which initializes various class variables depending on the arguments. @@ -187,45 +187,10 @@ def __init__(self,fmt,opnode,opcode,randomization, xl, fl,base_isa_str): global xlen global flen global base_isa - xlen = xl - flen = fl - base_isa = base_isa_str - self.fmt = fmt - self.opcode = opcode - - self.op_vars = OPS[fmt] - self.val_vars = eval(VALS[fmt]) - - if opcode in ['sw', 'sh', 'sb', 'lw', 'lhu', 'lh', 'lb', 'lbu', 'ld', 'lwu', 'sd',"jal","beq","bge","bgeu","blt","bltu","bne","jalr","flw","fsw","fld","fsd"]: - self.val_vars = self.val_vars + ['ea_align'] - self.template = opnode['template'] - self.opnode = opnode - self.stride = opnode['stride'] - if 'operation' in opnode: - self.operation = opnode['operation'] - else: - self.operation = None - datasets = {} - i=10 - for entry in self.op_vars: - key = entry+"_op_data" - if key in opnode: - datasets[entry] = eval(opnode[key]) - else: - datasets[entry] = ['x'+str(i)] - i+=1 - for entry in self.val_vars: - key = entry+"_data" - if key in opnode: - datasets[entry] = eval(opnode[key]) - else: - datasets[entry] = [0] - self.datasets = datasets - self.random=randomization - self.default_regs = get_default_registers(self.op_vars, self.datasets) - - def cross_comb(cgf, xlen): + base_isa = base_isa_str + + def cross_comb(cgf): ''' This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. @@ -238,7 +203,7 @@ def cross_comb(cgf, xlen): else: return - dntcare_instrs = isac_utils.import_instr_alias('rv' + str(xlen) + 'i_arith') + isac_utils.import_instr_alias('rv' + str(xlen) + 'i_shift') + dntcare_instrs = isac_utils.import_instr_alias(base_isa + '_arith') + isac_utils.import_instr_alias(base_isa + '_shift') # This function retrieves available operands in a string def get_oprs(opr_str): @@ -272,7 +237,7 @@ def get_oprs(opr_str): # Get corresponding conditions and accordingly chose instruction cond = cond_lst[i] - if cond.find('?') != -1: + if cond.find('?') != -1: # Don't care condition # Check variables in assignment list and generate required operand list assgn = assgn_lst[i] @@ -308,28 +273,57 @@ def get_oprs(opr_str): instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - # Choose instruction - print(instrs_sol) - - instr = instrs_sol[0] # For now + # Randomly choose an instruction + instr = random.choice(instrs_sol) + print(instr) # Choose operand values formattype = cross.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] + instr_template = cross.OP_TEMPLATE[instr] problem.reset() - problem.addVariables(oprs, list(range(32))) + for opr in oprs: + opr_dom = instr_template[opr + '_op_data'] + problem.addVariable(opr, eval(opr_dom)) + + # Since rd = x0 is a trivial operation, it has to be excluded + if 'rd' in oprs: + # exclude zeros + def exc_rd_zero(*oprs_lst): + pos = oprs.index('rd') + if oprs_lst[pos] == 'x0': + return False + return True + + problem.addConstraint(exc_rd_zero, oprs) + + # Add additional contraints if any + if cond.find('?') != -1: + opr_sols = problem.getSolutions() + else: + def add_cond(*oprs_lst): + i = 0 + for opr in oprs: + exec(opr + "='" + oprs_lst[i] + "'") + return eval(cond) + + problem.addConstraint(add_cond, oprs) + opr_sols = problem.getSolutions() + print('Got em') + + opr_vals = random.choice(opr_sols) + print(opr_vals) + + # Assign operand values to operands + for opr, val in opr_vals.items(): + exec(opr + "='" + val + "'") - # Add contraint if any - - ''' # Get assignments if any and execute them if assgn_lst[i] != '?': assgns = assgn_lst[i].split(';') for each in assgns: exec(each) - print(locals) -''' else: instr = data[i] # Get the instruction/alias @@ -349,9 +343,9 @@ def get_oprs(opr_str): instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - print(instrs_sol) + instr = random.choice(instrs_sol) - # Select and instruction + # Randomly select an instruction # Assign values to operands if cond.find('?') != -1: @@ -370,5 +364,6 @@ def get_oprs(opr_str): if __name__ == '__main__': - cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [? : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : ? : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} - get_it = cross.cross_comb(cross_cov, 32) \ No newline at end of file + cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [a = rd; c = rs1 : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : rs1 != c and rd == a : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + cross_test = cross('rv32i') + get_it = cross.cross_comb(cross_cov) \ No newline at end of file From 2bcb9aaafd48778b6c985c476c3f36e3b2120d37 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 19 May 2022 22:19:15 +0530 Subject: [PATCH 11/50] Complete operand value generation --- riscv_ctg/cross_comb.py | 112 ++++++++++++++++++++++++++++------------ 1 file changed, 79 insertions(+), 33 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index cfee2cfe..c24ae4f6 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -165,10 +165,6 @@ class cross(): # Template dictionary OP_TEMPLATE = utils.load_yaml(const.template_file) - - # Supporting data-structure for cross_comb - FORMAT_DICT = utils.gen_format_data() - INSTR_LST = utils.get_instr_list() def __init__(self, base_isa_str): ''' @@ -237,10 +233,11 @@ def get_oprs(opr_str): # Get corresponding conditions and accordingly chose instruction cond = cond_lst[i] + assgn = assgn_lst[i] + if cond.find('?') != -1: # Don't care condition # Check variables in assignment list and generate required operand list - assgn = assgn_lst[i] opr_lst = get_oprs(assgn) # Get possible instructions based on the operand list @@ -256,10 +253,9 @@ def get_oprs(opr_str): opr_lst = [] # Extract required operands from condition list - opr_lst = get_oprs(cond) + opr_lst += get_oprs(cond) # Extract required operands from assignment list - assgn = assgn_lst[i] opr_lst += get_oprs(assgn) # Remove redundant operands @@ -275,7 +271,6 @@ def get_oprs(opr_str): # Randomly choose an instruction instr = random.choice(instrs_sol) - print(instr) # Choose operand values formattype = cross.OP_TEMPLATE[instr]['formattype'] @@ -302,18 +297,20 @@ def exc_rd_zero(*oprs_lst): if cond.find('?') != -1: opr_sols = problem.getSolutions() else: - def add_cond(*oprs_lst): - i = 0 - for opr in oprs: - exec(opr + "='" + oprs_lst[i] + "'") - return eval(cond) - - problem.addConstraint(add_cond, oprs) + def add_cond(local_var): + def eval_conds(*oprs_lst): + i = 0 + for opr in oprs: + exec(opr + "='" + oprs_lst[i] + "'", local_var) + i = i + 1 + return eval(cond, locals(), local_var) + return eval_conds + + local_vars = locals() + problem.addConstraint(add_cond(local_vars), oprs) opr_sols = problem.getSolutions() - print('Got em') opr_vals = random.choice(opr_sols) - print(opr_vals) # Assign operand values to operands for opr, val in opr_vals.items(): @@ -325,16 +322,23 @@ def add_cond(*oprs_lst): for each in assgns: exec(each) + print(instr) + print(opr_vals) + else: - instr = data[i] # Get the instruction/alias cond = cond_lst[i] assgn = assgn_lst[i] - if instr in cross.OP_TEMPLATE: - formattype = cross.OP_TEMPLATE[instr]['formattype'] - oprs = OPS[formattype] + # Gather required operands + opr_lst = get_oprs(cond) + opr_lst += get_oprs(assgn) + + opr_lst = list(set(opr_lst)) + + if data[i] in cross.OP_TEMPLATE: + instr = data[i] else: - alias_instrs = isac_utils.import_instr_alias(instr) + alias_instrs = isac_utils.import_instr_alias(data[i]) if alias_instrs: problem.reset() problem.addVariable('i', alias_instrs) @@ -343,27 +347,69 @@ def add_cond(*oprs_lst): instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - instr = random.choice(instrs_sol) - # Randomly select an instruction + instr = random.choice(instrs_sol) + + else: + logger.error('Invalid instruction/alias in cross_comb: ' + each) + + formattype = cross.OP_TEMPLATE[instr]['formattype'] + oprs = OPS[formattype] + instr_template = cross.OP_TEMPLATE[instr] + + problem.reset() + for opr in oprs: + opr_dom = instr_template[opr + '_op_data'] + problem.addVariable(opr, eval(opr_dom)) + + # Since rd = x0 is a trivial operation, it has to be excluded + if 'rd' in oprs: + # exclude zeros + def exc_rd_zero(*oprs_lst): + pos = oprs.index('rd') + if oprs_lst[pos] == 'x0': + return False + return True + + problem.addConstraint(exc_rd_zero, oprs) # Assign values to operands if cond.find('?') != -1: - problem.reset() - problem.addVariables(oprs, list(range(32))) - + opr_sols = problem.getSolutions() else: - problem.reset() - problem.addVariables(oprs, list(range(32))) + def add_cond(local_var): + def eval_conds(*oprs_lst): + i = 0 + for opr in oprs: + exec(opr + "='" + oprs_lst[i] + "'", local_var) + i = i + 1 + return eval(cond, locals(), local_var) + return eval_conds + + local_vars = locals() + #problem.addConstraint(add_cond(local_vars), oprs) + opr_sols = problem.getSolutions() - # Add constraint + # Get operand values + opr_vals = random.choice(opr_sols) - # Get operand values + # Assign operand values to operands + for opr, val in opr_vals.items(): + exec(opr + "='" + val + "'") + + # Execute assignments + # Get assignments if any and execute them + if assgn_lst[i] != '?': + assgns = assgn_lst[i].split(';') + for each in assgns: + exec(each) + + print(instr) + print(opr_vals) - # Execute assignments if __name__ == '__main__': - cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [a = rd; c = rs1 : a=rd;b=rs1 : ? : ? : ?: ?] :: [? : rs1 != c and rd == a : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [a = rd; a = rs1 : a=rd;a=rs1 : ? : ? : ?: ?] :: [? : rs1 != a and rd == a : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} cross_test = cross('rv32i') get_it = cross.cross_comb(cross_cov) \ No newline at end of file From d222ce57fd8f53fd3ae78f873d03682896306a02 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 19 May 2022 22:45:19 +0530 Subject: [PATCH 12/50] Generation of immediates --- riscv_ctg/cross_comb.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index c24ae4f6..31e93ac6 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -166,7 +166,7 @@ class cross(): # Template dictionary OP_TEMPLATE = utils.load_yaml(const.template_file) - def __init__(self, base_isa_str): + def __init__(self, base_isa_str, xlen_in): ''' This is a Constructor function which initializes various class variables depending on the arguments. @@ -184,6 +184,7 @@ def __init__(self, base_isa_str): global flen global base_isa + xlen = xlen_in base_isa = base_isa_str def cross_comb(cgf): @@ -277,6 +278,7 @@ def get_oprs(opr_str): oprs = OPS[formattype] instr_template = cross.OP_TEMPLATE[instr] + # Choose register values problem.reset() for opr in oprs: opr_dom = instr_template[opr + '_op_data'] @@ -311,11 +313,15 @@ def eval_conds(*oprs_lst): opr_sols = problem.getSolutions() opr_vals = random.choice(opr_sols) - + # Assign operand values to operands for opr, val in opr_vals.items(): exec(opr + "='" + val + "'") + if 'imm_val_data' in cross.OP_TEMPLATE[instr]: + imm_val = eval(cross.OP_TEMPLATE[instr]['imm_val_data']) + opr_vals['imm_val'] = random.choice(imm_val) + # Get assignments if any and execute them if assgn_lst[i] != '?': assgns = assgn_lst[i].split(';') @@ -387,7 +393,7 @@ def eval_conds(*oprs_lst): return eval_conds local_vars = locals() - #problem.addConstraint(add_cond(local_vars), oprs) + problem.addConstraint(add_cond(local_vars), oprs) opr_sols = problem.getSolutions() # Get operand values @@ -397,6 +403,10 @@ def eval_conds(*oprs_lst): for opr, val in opr_vals.items(): exec(opr + "='" + val + "'") + if 'imm_val_data' in cross.OP_TEMPLATE[instr]: + imm_val = eval(cross.OP_TEMPLATE[instr]['imm_val_data']) + opr_vals['imm_val'] = random.choice(imm_val) + # Execute assignments # Get assignments if any and execute them if assgn_lst[i] != '?': @@ -411,5 +421,5 @@ def eval_conds(*oprs_lst): if __name__ == '__main__': cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [a = rd; a = rs1 : a=rd;a=rs1 : ? : ? : ?: ?] :: [? : rs1 != a and rd == a : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} - cross_test = cross('rv32i') + cross_test = cross('rv32i', 32) get_it = cross.cross_comb(cross_cov) \ No newline at end of file From e2489b45cfda99bee638b89013e97cdc988f1402 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 19 May 2022 23:23:32 +0530 Subject: [PATCH 13/50] Handle instruction lists --- riscv_ctg/cross_comb.py | 60 ++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 31e93ac6..262f656d 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,4 +1,5 @@ # See LICENSE.incore for details +from multiprocessing.sharedctypes import Value import random from collections import defaultdict from constraint import * @@ -183,7 +184,7 @@ def __init__(self, base_isa_str, xlen_in): global xlen global flen global base_isa - + xlen = xlen_in base_isa = base_isa_str @@ -200,6 +201,11 @@ def cross_comb(cgf): else: return + # Generate register file and variables + reg_file = ['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)] + for each in reg_file: + exec(f"{each} = '{each}'") + dntcare_instrs = isac_utils.import_instr_alias(base_isa + '_arith') + isac_utils.import_instr_alias(base_isa + '_shift') # This function retrieves available operands in a string @@ -215,8 +221,21 @@ def get_oprs(opr_str): opr_lst.append('rs3') return opr_lst - + + def add_cond(local_var): + def eval_conds(*oprs_lst): + i = 0 + for opr in oprs: + exec(opr + "='" + oprs_lst[i] + "'", local_var) + i = i + 1 + return eval(cond, locals(), local_var) + return eval_conds + + for each in cross_comb: + print('') + print(each) + parts = each.split('::') data = parts[0].replace(' ', '')[1:-1].split(':') @@ -299,15 +318,6 @@ def exc_rd_zero(*oprs_lst): if cond.find('?') != -1: opr_sols = problem.getSolutions() else: - def add_cond(local_var): - def eval_conds(*oprs_lst): - i = 0 - for opr in oprs: - exec(opr + "='" + oprs_lst[i] + "'", local_var) - i = i + 1 - return eval(cond, locals(), local_var) - return eval_conds - local_vars = locals() problem.addConstraint(add_cond(local_vars), oprs) opr_sols = problem.getSolutions() @@ -340,11 +350,11 @@ def eval_conds(*oprs_lst): opr_lst += get_oprs(assgn) opr_lst = list(set(opr_lst)) - - if data[i] in cross.OP_TEMPLATE: + + if data[i] in cross.OP_TEMPLATE: # If single instruction instr = data[i] else: - alias_instrs = isac_utils.import_instr_alias(data[i]) + alias_instrs = isac_utils.import_instr_alias(data[i]) # If data is an alias if alias_instrs: problem.reset() problem.addVariable('i', alias_instrs) @@ -355,7 +365,10 @@ def eval_conds(*oprs_lst): # Randomly select an instruction instr = random.choice(instrs_sol) - + + elif data[i].find('(') != -1: + instrs_sol = data[i][1:-1].split(',') + instr = random.choice(instrs_sol) else: logger.error('Invalid instruction/alias in cross_comb: ' + each) @@ -383,15 +396,6 @@ def exc_rd_zero(*oprs_lst): if cond.find('?') != -1: opr_sols = problem.getSolutions() else: - def add_cond(local_var): - def eval_conds(*oprs_lst): - i = 0 - for opr in oprs: - exec(opr + "='" + oprs_lst[i] + "'", local_var) - i = i + 1 - return eval(cond, locals(), local_var) - return eval_conds - local_vars = locals() problem.addConstraint(add_cond(local_vars), oprs) opr_sols = problem.getSolutions() @@ -420,6 +424,12 @@ def eval_conds(*oprs_lst): if __name__ == '__main__': - cross_cov = {'cross_comb' : {'[add : ? : mul : ? : rv32i_shift : sub ] :: [a = rd; a = rs1 : a=rd;a=rs1 : ? : ? : ?: ?] :: [? : rs1 != a and rd == a : rs1==a or rs2==a : ? : rs1==a or rs2==a: ?] ' : 0}} + cross_cov = {'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, + '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, + '[add : ? : ? : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, + '[(add,sub) : ? : ? : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, + '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 + } + } cross_test = cross('rv32i', 32) get_it = cross.cross_comb(cross_cov) \ No newline at end of file From 56259953ad34c6ec02ac7f01398d5da03771fb92 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 19 May 2022 23:44:17 +0530 Subject: [PATCH 14/50] Comments --- riscv_ctg/cross_comb.py | 108 +++++++++++++--------------------------- 1 file changed, 34 insertions(+), 74 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 262f656d..f2e4206b 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,9 +1,6 @@ # See LICENSE.incore for details -from multiprocessing.sharedctypes import Value import random -from collections import defaultdict from constraint import * -import re import riscv_isac.utils as isac_utils from riscv_ctg.constants import * @@ -11,16 +8,10 @@ import riscv_ctg.utils as utils import riscv_ctg.constants as const -import time from math import * -import struct -import sys -import itertools from riscv_ctg.dsp_function import * -twos_xlen = lambda x: twos(x,xlen) - OPS = { 'rformat': ['rs1', 'rs2', 'rd'], 'iformat': ['rs1', 'rd'], @@ -105,45 +96,6 @@ } ''' Dictionary mapping instruction formats to operand value variables used by those formats ''' -def isInt(s): - ''' - Utility function to check if the variable is an int type. Returns False if - not. - ''' - try: - int(s) - return True - except ValueError: - return False - -def get_default_registers(ops, datasets): - problem = Problem() - not_x0 = lambda x: x not in ['x0'] - - for op in ops: - dataset = datasets[op] - # problem.addVariable(op,list(random.sample(dataset, len(dataset)))) - problem.addVariable(op,dataset) - problem.addConstraint(not_x0,tuple([op])) - if len(ops) > 1: - cond = " and ".join(["!=".join(x) for x in itertools.combinations(ops,2) if x[0]!=x[1]]) - else: - cond = 'True' - def unique_constraint(*args): - for var,val in zip(ops,args): - locals()[var] = val - return eval(cond) - problem.addConstraint(unique_constraint,tuple(ops)) - solution = None - count = 0 - while solution is None and count < 5: - solution = problem.getSolution() - count += 1 - if count == 5: - return [] - else: - return solution - class cross(): ''' A generator class to generate RISC-V assembly tests for a given instruction @@ -193,9 +145,8 @@ def cross_comb(cgf): This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. ''' - #logger.debug(self.opcode + ': Generating CrossComb') - #solutions = [] - + logger.debug('Generating CrossComb') + if 'cross_comb' in cgf: cross_comb = set(cgf['cross_comb']) else: @@ -222,6 +173,7 @@ def get_oprs(opr_str): return opr_lst + # Add conditions mentioned in the condition list in the cross-comb coverpoint def add_cond(local_var): def eval_conds(*oprs_lst): i = 0 @@ -231,25 +183,27 @@ def eval_conds(*oprs_lst): return eval(cond, locals(), local_var) return eval_conds - for each in cross_comb: print('') print(each) + # Parse cross-comb coverpoint parts = each.split('::') - + data = parts[0].replace(' ', '')[1:-1].split(':') assgn_lst = parts[1].replace(' ', '')[1:-1].split(':') cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') - - i = 0 + + # Initialize CSP problem = Problem() + for i in range(len(data)): if data[i] == '?': # When instruction is not specified, - # - Gather conditions if any - # - Choose instruction based on operands in condition list - # - Generate assignments + # - Gather conditions and assigngments if any and list requisite operands + # - Choose instruction from base instruction set based on operands + # - Based on conditions, choose operand values. Choose immediate value if required + # - Evaluate assignments # Get corresponding conditions and accordingly chose instruction cond = cond_lst[i] @@ -268,7 +222,7 @@ def eval_conds(*oprs_lst): instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - else: + else: # If condition is specified opr_lst = [] @@ -328,8 +282,9 @@ def exc_rd_zero(*oprs_lst): for opr, val in opr_vals.items(): exec(opr + "='" + val + "'") - if 'imm_val_data' in cross.OP_TEMPLATE[instr]: - imm_val = eval(cross.OP_TEMPLATE[instr]['imm_val_data']) + # Generate immediate value if required + if 'imm_val_data' in instr_template: + imm_val = eval(instr_template['imm_val_data']) opr_vals['imm_val'] = random.choice(imm_val) # Get assignments if any and execute them @@ -338,10 +293,15 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) - print(instr) - print(opr_vals) + print([instr, opr_vals]) else: + # When instruction(s)/alias is specified, + # - If an instruction is specified, operands are directly extracted and assigned values according to conditions + # - If a tuple of instructions is specified, one of the instruction is chosen at random + # - If an alias is specified, the instruction is chosen according to assignment and condition list + # - Immediate values are generated if required + # - Assignments are evaluated cond = cond_lst[i] assgn = assgn_lst[i] @@ -366,12 +326,13 @@ def exc_rd_zero(*oprs_lst): # Randomly select an instruction instr = random.choice(instrs_sol) - elif data[i].find('(') != -1: + elif data[i].find('(') != -1: # If data is a tuple of instructions instrs_sol = data[i][1:-1].split(',') instr = random.choice(instrs_sol) else: logger.error('Invalid instruction/alias in cross_comb: ' + each) + # Gather operands formattype = cross.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] instr_template = cross.OP_TEMPLATE[instr] @@ -407,8 +368,9 @@ def exc_rd_zero(*oprs_lst): for opr, val in opr_vals.items(): exec(opr + "='" + val + "'") - if 'imm_val_data' in cross.OP_TEMPLATE[instr]: - imm_val = eval(cross.OP_TEMPLATE[instr]['imm_val_data']) + # Generate immediate value if required + if 'imm_val_data' in instr_template: + imm_val = eval(instr_template['imm_val_data']) opr_vals['imm_val'] = random.choice(imm_val) # Execute assignments @@ -418,17 +380,15 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) - print(instr) - print(opr_vals) - + print([instr, opr_vals]) if __name__ == '__main__': - cross_cov = {'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, - '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, - '[add : ? : ? : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, - '[(add,sub) : ? : ? : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, - '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 + cross_cov = {'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW + '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW + '[add : ? : ? : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW + '[(add,sub) : ? : ? : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW + '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR } } cross_test = cross('rv32i', 32) From c34716b0edac46e2d217cf9f0474264f4f578d35 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Fri, 20 May 2022 00:07:17 +0530 Subject: [PATCH 15/50] Code cleanup --- riscv_ctg/cross_comb.py | 47 ++++++++++++----------------------------- 1 file changed, 14 insertions(+), 33 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index f2e4206b..210a37d7 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -8,8 +8,6 @@ import riscv_ctg.utils as utils import riscv_ctg.constants as const -from math import * - from riscv_ctg.dsp_function import * OPS = { @@ -98,41 +96,13 @@ class cross(): ''' - A generator class to generate RISC-V assembly tests for a given instruction - format, opcode and a set of coverpoints. - - :param fmt: the RISC-V instruction format type to be used for the test generation. - :param opnode: dictionary node from the attributes YAML that is to be used in the test generation. - :param opcode: name of the instruction opcode. - :param randomization: a boolean variable indicating if the random constraint solvers must be employed. - :param xl: an integer indicating the XLEN value to be used. - :param base_isa_str: The base isa to be used for the tests. One of [rv32e,rv32i,rv64i] - - :type fmt: str - :type opnode: dict - :type opcode: str - :type randomization: bool - :type xl: int - :type base_isa_str: str + A cross class to genereate RISC-V assembly tests for cross-combination coverpoints. ''' # Template dictionary OP_TEMPLATE = utils.load_yaml(const.template_file) def __init__(self, base_isa_str, xlen_in): - ''' - This is a Constructor function which initializes various class variables - depending on the arguments. - - The function also creates a dictionary of datasets for each operand. The - dictionary basically indicates what registers from the register file are to be used - when generating solutions for coverpoints. The datasets are limited to - to reduce the time taken by solvers to arrive at a solution. - - A similar dictionary is created for the values to be used by the operand - registers. - - ''' global xlen global flen global base_isa @@ -146,6 +116,7 @@ def cross_comb(cgf): in the CGF under the `cross_comb` node of the covergroup. ''' logger.debug('Generating CrossComb') + full_solution = [] if 'cross_comb' in cgf: cross_comb = set(cgf['cross_comb']) @@ -183,10 +154,13 @@ def eval_conds(*oprs_lst): return eval(cond, locals(), local_var) return eval_conds + solution = [] for each in cross_comb: print('') print(each) + solution = [] + # Parse cross-comb coverpoint parts = each.split('::') @@ -293,6 +267,7 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) + solution += [instr, opr_vals] print([instr, opr_vals]) else: @@ -380,16 +355,22 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) + solution += [instr, opr_vals] print([instr, opr_vals]) + + full_solution += [solution] + + return full_solution if __name__ == '__main__': cross_cov = {'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW '[add : ? : ? : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW - '[(add,sub) : ? : ? : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW + '[(add,sub) : ? : mul : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR } } cross_test = cross('rv32i', 32) - get_it = cross.cross_comb(cross_cov) \ No newline at end of file + get_it = cross.cross_comb(cross_cov) + print(get_it) \ No newline at end of file From a18b3aebb5d1449d26a233c813ec29ef026c5539 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 12:49:41 +0530 Subject: [PATCH 16/50] Cleanup --- riscv_ctg/ctg.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index 0fe19584..d1ed4200 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -92,8 +92,4 @@ def ctg(verbose, out, random ,xlen_arg, cgf_file,num_procs,base_isa, max_inst): cgf = expand_cgf(cgf_file,xlen) pool = mp.Pool(num_procs) results = pool.starmap(create_test, [(usage_str, node,label,base_isa,max_inst) for label,node in cgf.items()]) - pool.close() - -if __name__ == '__main__': - gen_format_data() - print(locals()) \ No newline at end of file + pool.close() \ No newline at end of file From 3bf000737907ca23e4d81c292bb118014aac1d01 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 12:50:08 +0530 Subject: [PATCH 17/50] Generate test --- riscv_ctg/cross_comb.py | 176 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 158 insertions(+), 18 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 210a37d7..b71ff3ce 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,4 +1,6 @@ # See LICENSE.incore for details +import time +import pprint import random from constraint import * @@ -7,6 +9,7 @@ from riscv_ctg.log import logger import riscv_ctg.utils as utils import riscv_ctg.constants as const +from riscv_ctg.__init__ import __version__ from riscv_ctg.dsp_function import * @@ -94,6 +97,47 @@ } ''' Dictionary mapping instruction formats to operand value variables used by those formats ''' +INSTR_FORMAT = { + 'rformat': '$instr $rd, $rs1, $rs2', + 'iformat': '$instr $rd, $rs1, $imm_val' +} + +REG_INIT = ''' +LI (x1, (0xFEEDBEADFEEDBEAD & MASK)); +LI (x2, (0xFF76DF56FF76DF56 & MASK)); +LI (x3, (0x7FBB6FAB7FBB6FAB & MASK)); +LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK)); +LA (x5, (0xAB7FFB6FAB7FBB6F & MASK)); +LA (x6, (0x6FAB71BB6F7B7FBB & MASK)); +LI (x7, (0xB7FBB6FAB7FBB6FA & MASK)); +LI (x8, (0x5BFDDB7D5BFDDB7D & MASK)); +LI (x9, (0xADFEEDBEADFEEDBE & MASK)); +LI (x10, (0x56FF76DF56FF76DF & MASK)); +LI (x11, (0xAB7FBB6FAB7FBB6F & MASK)); +LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK)); +LI (x13, (0xEADFEEDBEADFEEDB & MASK)); +LI (x14, (0xF56FF76DF56FF76D & MASK)); +LI (x15, (0xFAB7FBB6FAB7FBB6 & MASK)); +#ifndef RVTEST_E +LI (x16, (0x7D5BFDDB7D5BFDDB & MASK)); +LI (x17, (0xBEADFEEDBEADFEED & MASK)); +LI (x18, (0xDF56FF76DF56FF76 & MASK)); +LI (x19, (0x6FAB7FBB6FAB7FBB & MASK)); +LI (x20, (0xB7D5BFDDB7D5BFDD & MASK)); +LI (x21, (0xDBEADFEEDBEADFEE & MASK)); +LI (x22, (0x6DF56FF76DF56FF7 & MASK)); +LI (x23, (0xB6FAB7FBB6FAB7FB & MASK)); +LI (x24, (0xDB7D5BFDDB7D5BFD & MASK)); +LI (x25, (0xEDBEADFEEDBEADFE & MASK)); +LI (x26, (0x76DF56FF76DF56FF & MASK)); +LI (x27, (0xBB6FAB7FBB6FAB7F & MASK)); +LI (x28, (0xDDB7D5BFDDB7D5BF & MASK)); +LI (x29, (0xEEDBEADFEEDBEADF & MASK)); +LI (x30, (0xF76DF56FF76DF56F & MASK)); +LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK)); +#endif +''' + class cross(): ''' A cross class to genereate RISC-V assembly tests for cross-combination coverpoints. @@ -110,7 +154,7 @@ def __init__(self, base_isa_str, xlen_in): xlen = xlen_in base_isa = base_isa_str - def cross_comb(cgf): + def cross_comb(cgf_node): ''' This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. @@ -118,8 +162,8 @@ def cross_comb(cgf): logger.debug('Generating CrossComb') full_solution = [] - if 'cross_comb' in cgf: - cross_comb = set(cgf['cross_comb']) + if 'cross_comb' in cgf_node: + cross_comb = set(cgf_node['cross_comb']) else: return @@ -156,8 +200,6 @@ def eval_conds(*oprs_lst): solution = [] for each in cross_comb: - print('') - print(each) solution = [] @@ -267,8 +309,8 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) - solution += [instr, opr_vals] - print([instr, opr_vals]) + opr_vals['instr'] = instr + solution += [opr_vals] else: # When instruction(s)/alias is specified, @@ -355,22 +397,120 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) - solution += [instr, opr_vals] - print([instr, opr_vals]) + opr_vals['instr'] = instr + solution += [opr_vals] full_solution += [solution] return full_solution + def swreg(cross_comb_instrs): + ''' + This function generates the register which can be used as a signature pointer for each instruction + ''' + + global base_isa + + op_vals = ['x0'] + + for instr_dict in cross_comb_instrs: + for key, val in instr_dict.items(): + if key != 'instr' and key != 'imm_val': + op_vals += val + + problem = Problem() + problem.addVariable('o', ['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)]) + problem.addConstraint(lambda op: op not in op_vals) + + swreg_sol = problem.getSolutions() + swreg_sol = [list(each.items())[0][1] for each in swreg_sol] + + sreg = random.choice(swreg_sol) + + return sreg + + def write_test(cov_label, isa, xlen, full_solution): + + code = '\n' + data = [".align 4","rvtest_data:",".word 0xbabecafe", \ + ".word 0xabecafeb", ".word 0xbecafeba", ".word 0xecafebab"] + sig = [''] + sreg_dict = dict() + # Handle solutions related to each cross combination coverpoint + for cross_sol in full_solution: + + # Designate signature update register + sreg = cross.swreg(cross_sol) + + # Designate count of sreg for signature label generation + if sreg not in sreg_dict: + sreg_dict[sreg] = 0 + else: + count = sreg_dict[sreg] + 1 + sreg_dict[sreg] = count + + sig_label = "signature_" + sreg + "_" + str(sreg_dict[sreg]) + code = code + "\nRVTEST_SIGBASE(" + sreg + ", "+ sig_label + ")\n\n" + + rd_lst = set() + # Generate instruction corresponding to each instruction dictionary + # Append signature update statements to store rd value after each instruction + code += '// Cross-combination test sequence\n' + for each in cross_sol: + + if 'rd' in each: + rd_lst.add(each['rd']) + + instr_str_format = Template(INSTR_FORMAT[cross.OP_TEMPLATE[each['instr']]['formattype']]) + instr_str = instr_str_format.substitute(each) + code = code + instr_str + '\n' + + # Append .fill assembly directives to initialize signature regions + sig.append(sig_label + ':\n\t.fill ' + str(len(rd_lst)) + ', 4, 0xdeadbeef\n') + + offset = 0 + code += '\n// Store destination register values in the test signature region\n' + # Add signature update statement(s) for unique number of rds + for rd in rd_lst: + sig_upd = f'RVTEST_SIGUPD({sreg}, {rd}, {offset})' + offset = offset + int(xlen/8) + code = code + sig_upd + '\n' + + # Initialize registers for next cross-comb coverpoint + code = code + REG_INIT + + case_str = ''.join([case_template.safe_substitute(xlen=xlen,num=i,cond=cond,cov_label=cov_label) for i,cond in enumerate(node['config'])]) + test = part_template.safe_substitute(case_str=case_str,code=code) + + # Write test to file + with open('test.S', 'w') as fp: + mytime = time.asctime(time.gmtime(time.time()) ) + ' GMT' + fp.write(const.usage.safe_substitute(base_isa = isa, + version = __version__, + time = mytime, + xlen = xlen + ) + ) + fp.write(const.test_template.safe_substitute(opcode = cov_label, + isa = isa, + test = test, + data = '\n'.join(data), + sig = '\n'.join(sig) + ) + ) + if __name__ == '__main__': - cross_cov = {'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW - '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW - '[add : ? : ? : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW - '[(add,sub) : ? : mul : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW - '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR - } - } + cov_node = 'add' + isa = 'RV32I' + node = {'config': {'check ISA:=regex(.*I.*)'}, + 'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW + '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW + '[add : ? : rv32i_shift : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW + '[(add,sub) : ? : mul : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW + '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR + } + } cross_test = cross('rv32i', 32) - get_it = cross.cross_comb(cross_cov) - print(get_it) \ No newline at end of file + full_solution = cross.cross_comb(node) + print_instr = cross.write_test(cov_node, isa, 32, full_solution) \ No newline at end of file From bd43afd5dca5ee7a757076bd078e09b7d615d44d Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 13:37:23 +0530 Subject: [PATCH 18/50] Upstream fetch --- riscv_ctg/ctg.py | 53 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index d1ed4200..792f48de 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -1,5 +1,6 @@ # See LICENSE.incore file for details +import copy import os,re import multiprocessing as mp @@ -20,28 +21,20 @@ def create_test(usage_str, node,label,base_isa,max_inst): global xlen flen = 0 - if 'opcode' not in node: + if 'mnemonics' not in node: + logger.warning("mnemonics node not found in covergroup: " + str(label)) return if 'ignore' in node: logger.info("Ignoring :" + str(label)) if node['ignore']: return - for opcode in node['opcode']: - op_node=None - if opcode not in op_template: - for op,foo in op_template.items(): - if op!='metadata' and foo['std_op'] is not None and opcode==foo['std_op']: - op_node = foo - break - else: - op_node = op_template[opcode] - - if op_node is None: - logger.warning("Skipping :" + str(opcode)) - return + + # Function to encompass checks and test generation + def gen_test(op_node, opcode): if xlen not in op_node['xlen']: logger.warning("Skipping {0} since its not supported in current XLEN:".format(opcode)) return + flen = 0 if 'flen' in op_node: if '.d' in opcode: flen = 64 @@ -62,6 +55,38 @@ def create_test(usage_str, node,label,base_isa,max_inst): my_dict = gen.reformat_instr(instr_dict) gen.write_test(fprefix,node,label,my_dict, op_node, usage_str, max_inst) + # If base_op defined in covergroup, extract corresponding template + # else go through the instructions defined in mnemonics label + op_node = None + if 'base_op' in node: + # Extract pseudo and base instructions + base_op = node['base_op'] + pseudop = list(node['mnemonics'].keys())[0] + if base_op in op_template and pseudop in op_template: + op_node = copy.deepcopy(op_template[base_op]) + pseudo_template = op_template[pseudop] + + # Ovewrite/add nodes from pseudoinstruction template in base instruction template + for key, val in pseudo_template.items(): + op_node[key] = val + + # Generate tests + gen_test(op_node, pseudop) + else: + for opcode in node['mnemonics']: + if opcode in op_template: + op_node = op_template[opcode] + # Generate tests + gen_test(op_node, opcode) + else: + logger.warning(str(opcode) + " not found in template file. Skipping") + return + + # Return if there is no corresponding template + if op_node is None: + logger.warning("Skipping :" + str(opcode)) + return + def ctg(verbose, out, random ,xlen_arg, cgf_file,num_procs,base_isa, max_inst): global op_template global randomize From 5b9495c1ceed87a9f5bc574c8706d3d7a1d3e00a Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 15:24:08 +0530 Subject: [PATCH 19/50] Say hello to cross-comb :) --- riscv_ctg/ctg.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index 792f48de..03ffeaec 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -11,6 +11,7 @@ import riscv_ctg.constants as const from riscv_isac.cgf_normalize import expand_cgf from riscv_ctg.generator import Generator +from riscv_ctg.cross_comb import cross from math import * from riscv_ctg.__init__ import __version__ @@ -82,6 +83,12 @@ def gen_test(op_node, opcode): logger.warning(str(opcode) + " not found in template file. Skipping") return + if 'cross_comb' in node: + fprefix = os.path.join(out_dir,str(label)) + cross_obj = cross(base_isa, xlen, randomize, label) + cross_instr_dict = cross_obj.cross_comb(node) + cross_obj.write_test(fprefix, usage_str, label, cross_instr_dict) + # Return if there is no corresponding template if op_node is None: logger.warning("Skipping :" + str(opcode)) From 2bad0da107a457acc2cb694cc382b27a974d0b54 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 15:24:24 +0530 Subject: [PATCH 20/50] Banner for cross-comb --- riscv_ctg/constants.py | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/riscv_ctg/constants.py b/riscv_ctg/constants.py index 7dbad50e..27f430b4 100644 --- a/riscv_ctg/constants.py +++ b/riscv_ctg/constants.py @@ -215,6 +215,10 @@ def gen_bitmanip_dataset(bit_width,sign=True): // This assembly file tests the $opcode instruction of the RISC-V $extension extension for the $label covergroup. // ''' +cross_comment_template = ''' +// This assembly file is used for the test of cross-combination coverpoint described in $label covergroup. +''' + test_template = Template(copyright_string + comment_template+''' #include "model_test.h" #include "arch_test.h" @@ -238,6 +242,31 @@ def gen_bitmanip_dataset(bit_width,sign=True): $sig RVMODEL_DATA_END ''') + +cross_test_template = Template(copyright_string + cross_comment_template+''' +#include "model_test.h" +#include "arch_test.h" +RVTEST_ISA("$isa") + +.section .text.init +.globl rvtest_entry_point +rvtest_entry_point: +RVMODEL_BOOT +RVTEST_CODE_BEGIN +$test + +RVTEST_CODE_END +RVMODEL_HALT + +RVTEST_DATA_BEGIN +$data +RVTEST_DATA_END + +RVMODEL_DATA_BEGIN +$sig +RVMODEL_DATA_END +''') + case_template = Template(''' RVTEST_CASE($num,"//check ISA:=regex(.*$xlen.*);$cond;def TEST_CASE_1=True;",$cov_label) ''') From c81e79c448056c1500324e065a6ab05a35a0515b Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 15:24:41 +0530 Subject: [PATCH 21/50] Integration --- riscv_ctg/cross_comb.py | 164 +++++++++++++--------------------------- 1 file changed, 54 insertions(+), 110 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index b71ff3ce..d5b7f5d4 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,102 +1,18 @@ # See LICENSE.incore for details import time -import pprint import random from constraint import * import riscv_isac.utils as isac_utils -from riscv_ctg.constants import * -from riscv_ctg.log import logger + import riscv_ctg.utils as utils import riscv_ctg.constants as const +from riscv_ctg.constants import * +from riscv_ctg.log import logger from riscv_ctg.__init__ import __version__ - +from riscv_ctg.generator import OPS from riscv_ctg.dsp_function import * -OPS = { - 'rformat': ['rs1', 'rs2', 'rd'], - 'iformat': ['rs1', 'rd'], - 'sformat': ['rs1', 'rs2'], - 'bsformat': ['rs1', 'rs2', 'rd'], - 'bformat': ['rs1', 'rs2'], - 'uformat': ['rd'], - 'jformat': ['rd'], - 'crformat': ['rs1', 'rs2'], - 'cmvformat': ['rd', 'rs2'], - 'ciformat': ['rd'], - 'cssformat': ['rs2'], - 'ciwformat': ['rd'], - 'clformat': ['rd', 'rs1'], - 'csformat': ['rs1', 'rs2'], - 'caformat': ['rs1', 'rs2'], - 'cbformat': ['rs1'], - 'cjformat': [], - 'kformat': ['rs1','rd'], - 'frformat': ['rs1', 'rs2', 'rd'], - 'fsrformat': ['rs1', 'rd'], - 'fr4format': ['rs1', 'rs2', 'rs3', 'rd'], - 'pbrrformat': ['rs1', 'rs2', 'rd'], - 'phrrformat': ['rs1', 'rs2', 'rd'], - 'pbrformat': ['rs1', 'rd'], - 'phrformat': ['rs1', 'rd'], - 'pbriformat': ['rs1', 'rd'], - 'phriformat': ['rs1', 'rd'], - 'psbrrformat': ['rs1', 'rs2', 'rd'], - 'pshrrformat': ['rs1', 'rs2', 'rd'], - 'pwrrformat': ['rs1', 'rs2', 'rd'], - 'pwriformat': ['rs1', 'rd'], - 'pwrformat': ['rs1', 'rd'], - 'pswrrformat': ['rs1', 'rs2', 'rd'], - 'pwhrrformat': ['rs1', 'rs2', 'rd'], - 'pphrrformat': ['rs1', 'rs2', 'rd'], - 'ppbrrformat': ['rs1', 'rs2', 'rd'], - 'prrformat': ['rs1', 'rs2', 'rd'], - 'prrrformat': ['rs1', 'rs2', 'rs3', 'rd'] -} -''' Dictionary mapping instruction formats to operands used by those formats ''' - -VALS = { - 'rformat': "['rs1_val', 'rs2_val']", - 'iformat': "['rs1_val', 'imm_val']", - 'sformat': "['rs1_val', 'rs2_val', 'imm_val']", - 'bsformat': "['rs1_val', 'rs2_val', 'imm_val']", - 'bformat': "['rs1_val', 'rs2_val', 'imm_val']", - 'uformat': "['imm_val']", - 'jformat': "['imm_val']", - 'crformat': "['rs1_val', 'rs2_val']", - 'cmvformat': "['rs2_val']", - 'ciformat': "['rs1_val', 'imm_val']", - 'cssformat': "['rs2_val', 'imm_val']", - 'ciwformat': "['imm_val']", - 'clformat': "['rs1_val', 'imm_val']", - 'csformat': "['rs1_val', 'rs2_val', 'imm_val']", - 'caformat': "['rs1_val', 'rs2_val']", - 'cbformat': "['rs1_val', 'imm_val']", - 'cjformat': "['imm_val']", - 'kformat': "['rs1_val']", - 'frformat': "['rs1_val', 'rs2_val', 'rm_val']", - 'fsrformat': "['rs1_val', 'rm_val']", - 'fr4format': "['rs1_val', 'rs2_val', 'rs3_val', 'rm_val']", - 'pbrrformat': 'simd_val_vars("rs1", xlen, 8) + simd_val_vars("rs2", xlen, 8)', - 'phrrformat': 'simd_val_vars("rs1", xlen, 16) + simd_val_vars("rs2", xlen, 16)', - 'pbrformat': 'simd_val_vars("rs1", xlen, 8)', - 'phrformat': 'simd_val_vars("rs1", xlen, 16)', - 'pbriformat': 'simd_val_vars("rs1", xlen, 8) + ["imm_val"]', - 'phriformat': 'simd_val_vars("rs1", xlen, 16) + ["imm_val"]', - 'psbrrformat': 'simd_val_vars("rs1", xlen, 8) + ["rs2_val"]', - 'pshrrformat': 'simd_val_vars("rs1", xlen, 16) + ["rs2_val"]', - 'pwrrformat': 'simd_val_vars("rs1", xlen, 32) + simd_val_vars("rs2", xlen, 32)', - 'pwriformat': 'simd_val_vars("rs1", xlen, 32) + ["imm_val"]', - 'pwrformat': 'simd_val_vars("rs1", xlen, 32)', - 'pswrrformat': 'simd_val_vars("rs1", xlen, 32) + ["rs2_val"]', - 'pwhrrformat': 'simd_val_vars("rs1", xlen, 32) + simd_val_vars("rs2", xlen, 16)', - 'pphrrformat': '["rs1_val"] + simd_val_vars("rs2", xlen, 16)', - 'ppbrrformat': '["rs1_val"] + simd_val_vars("rs2", xlen, 8)', - 'prrformat': '["rs1_val", "rs2_val"]', - 'prrrformat': "['rs1_val', 'rs2_val' , 'rs3_val']" -} -''' Dictionary mapping instruction formats to operand value variables used by those formats ''' - INSTR_FORMAT = { 'rformat': '$instr $rd, $rs1, $rs2', 'iformat': '$instr $rd, $rs1, $imm_val' @@ -146,15 +62,18 @@ class cross(): # Template dictionary OP_TEMPLATE = utils.load_yaml(const.template_file) - def __init__(self, base_isa_str, xlen_in): + def __init__(self, base_isa_str, xlen_in, randomize, label): global xlen global flen global base_isa xlen = xlen_in base_isa = base_isa_str + + self.randomize = randomize + self.label = label - def cross_comb(cgf_node): + def cross_comb(self, cgf_node): ''' This function finds solution for various cross-combinations defined by the coverpoints in the CGF under the `cross_comb` node of the covergroup. @@ -167,8 +86,10 @@ def cross_comb(cgf_node): else: return + isa_set = [] + # Generate register file and variables - reg_file = ['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)] + reg_file = const.default_regset for each in reg_file: exec(f"{each} = '{each}'") @@ -211,7 +132,10 @@ def eval_conds(*oprs_lst): cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') # Initialize CSP - problem = Problem() + if self.randomize: + problem = Problem(MinConflictsSolver) + else: + problem = Problem() for i in range(len(data)): if data[i] == '?': @@ -261,7 +185,8 @@ def eval_conds(*oprs_lst): # Randomly choose an instruction instr = random.choice(instrs_sol) - + isa_set += (cross.OP_TEMPLATE[instr]['isa']) + # Choose operand values formattype = cross.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] @@ -396,12 +321,14 @@ def exc_rd_zero(*oprs_lst): assgns = assgn_lst[i].split(';') for each in assgns: exec(each) - + + isa_set += (cross.OP_TEMPLATE[instr]['isa']) opr_vals['instr'] = instr solution += [opr_vals] full_solution += [solution] + self.isa = list(set(isa_set)) return full_solution def swreg(cross_comb_instrs): @@ -429,13 +356,21 @@ def swreg(cross_comb_instrs): return sreg - def write_test(cov_label, isa, xlen, full_solution): + def write_test(self, fprefix, usage_str, cov_label, full_solution): code = '\n' data = [".align 4","rvtest_data:",".word 0xbabecafe", \ ".word 0xabecafeb", ".word 0xbecafeba", ".word 0xecafebab"] sig = [''] sreg_dict = dict() + + # Generate ISA and extension string + extension = "" + rvxlen = "RV"+str(xlen) + op_node_isa = ",".join([rvxlen + isa for isa in self.isa]) + op_node_isa = op_node_isa.replace("I","E") if 'e' in base_isa else op_node_isa + extension = op_node_isa.replace('I',"").replace('E',"") + # Handle solutions related to each cross combination coverpoint for cross_sol in full_solution: @@ -479,23 +414,19 @@ def write_test(cov_label, isa, xlen, full_solution): # Initialize registers for next cross-comb coverpoint code = code + REG_INIT - case_str = ''.join([case_template.safe_substitute(xlen=xlen,num=i,cond=cond,cov_label=cov_label) for i,cond in enumerate(node['config'])]) - test = part_template.safe_substitute(case_str=case_str,code=code) + case_str = ''.join([case_template.safe_substitute(xlen = xlen,num = i, cond = cond, cov_label = cov_label) for i, cond in enumerate(node['config'])]) + test = part_template.safe_substitute(case_str = case_str, code = code) # Write test to file - with open('test.S', 'w') as fp: + with open(fprefix + f'/{cov_label}_cross-comb.S', 'w') as fp: mytime = time.asctime(time.gmtime(time.time()) ) + ' GMT' - fp.write(const.usage.safe_substitute(base_isa = isa, - version = __version__, - time = mytime, - xlen = xlen - ) - ) - fp.write(const.test_template.safe_substitute(opcode = cov_label, - isa = isa, + fp.write(usage_str + const.cross_test_template.safe_substitute(opcode = cov_label, + isa = op_node_isa, test = test, data = '\n'.join(data), - sig = '\n'.join(sig) + sig = '\n'.join(sig), + label = cov_label, + extension = extension ) ) @@ -503,6 +434,10 @@ def write_test(cov_label, isa, xlen, full_solution): cov_node = 'add' isa = 'RV32I' + cgf_arg = '/path/to/cgf/' + rand_arg = True + xlen = 32 + fprefix = '.' node = {'config': {'check ISA:=regex(.*I.*)'}, 'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW @@ -511,6 +446,15 @@ def write_test(cov_label, isa, xlen, full_solution): '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR } } - cross_test = cross('rv32i', 32) - full_solution = cross.cross_comb(node) - print_instr = cross.write_test(cov_node, isa, 32, full_solution) \ No newline at end of file + + mytime = time.asctime(time.gmtime(time.time()) ) + ' GMT' + usage_str = const.usage.safe_substitute(version = __version__, + time = mytime, + xlen = xlen, + cgf = cgf_arg, + randomize = rand_arg + ) + + cross_test = cross('rv32i', 32, False, cov_node) + full_solution = cross_test.cross_comb(node) + cross_test.write_test(fprefix, usage_str, cov_node, full_solution) \ No newline at end of file From 94c68903e0e75767c804e4134a7d8e7ab4a8244e Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Wed, 25 May 2022 19:36:31 +0530 Subject: [PATCH 22/50] Cleanup --- riscv_ctg/cross_comb.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index d5b7f5d4..e870a813 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -356,7 +356,7 @@ def swreg(cross_comb_instrs): return sreg - def write_test(self, fprefix, usage_str, cov_label, full_solution): + def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): code = '\n' data = [".align 4","rvtest_data:",".word 0xbabecafe", \ @@ -414,12 +414,11 @@ def write_test(self, fprefix, usage_str, cov_label, full_solution): # Initialize registers for next cross-comb coverpoint code = code + REG_INIT - case_str = ''.join([case_template.safe_substitute(xlen = xlen,num = i, cond = cond, cov_label = cov_label) for i, cond in enumerate(node['config'])]) + case_str = ''.join([case_template.safe_substitute(xlen = xlen,num = i, cond = cond, cov_label = cov_label) for i, cond in enumerate(cgf_node['config'])]) test = part_template.safe_substitute(case_str = case_str, code = code) # Write test to file with open(fprefix + f'/{cov_label}_cross-comb.S', 'w') as fp: - mytime = time.asctime(time.gmtime(time.time()) ) + ' GMT' fp.write(usage_str + const.cross_test_template.safe_substitute(opcode = cov_label, isa = op_node_isa, test = test, @@ -457,4 +456,4 @@ def write_test(self, fprefix, usage_str, cov_label, full_solution): cross_test = cross('rv32i', 32, False, cov_node) full_solution = cross_test.cross_comb(node) - cross_test.write_test(fprefix, usage_str, cov_node, full_solution) \ No newline at end of file + cross_test.write_test(fprefix, node, usage_str, cov_node, full_solution) \ No newline at end of file From 6032524d861a8876147001c4aff3a00aab44cbc1 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Fri, 27 May 2022 10:53:10 +0530 Subject: [PATCH 23/50] Handle dataset node --- riscv_ctg/ctg.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index 03ffeaec..eb83e3f7 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -22,7 +22,7 @@ def create_test(usage_str, node,label,base_isa,max_inst): global xlen flen = 0 - if 'mnemonics' not in node: + if 'mnemonics' not in node and label != 'datasets': logger.warning("mnemonics node not found in covergroup: " + str(label)) return if 'ignore' in node: @@ -87,7 +87,7 @@ def gen_test(op_node, opcode): fprefix = os.path.join(out_dir,str(label)) cross_obj = cross(base_isa, xlen, randomize, label) cross_instr_dict = cross_obj.cross_comb(node) - cross_obj.write_test(fprefix, usage_str, label, cross_instr_dict) + cross_obj.write_test(fprefix, node, usage_str, label, cross_instr_dict) # Return if there is no corresponding template if op_node is None: @@ -122,6 +122,8 @@ def ctg(verbose, out, random ,xlen_arg, cgf_file,num_procs,base_isa, max_inst): randomize=randomize_argument,xlen=str(xlen_arg)) op_template = utils.load_yaml(const.template_file) cgf = expand_cgf(cgf_file,xlen) + if 'datasets' in cgf: + del cgf['datasets'] pool = mp.Pool(num_procs) results = pool.starmap(create_test, [(usage_str, node,label,base_isa,max_inst) for label,node in cgf.items()]) pool.close() \ No newline at end of file From 9d1027075db8b2c30e75dbbc9738dde4157bb210 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Sun, 29 May 2022 15:30:18 +0530 Subject: [PATCH 24/50] Instruction formats --- riscv_ctg/cross_comb.py | 53 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index e870a813..9c3cca18 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,5 +1,8 @@ # See LICENSE.incore for details + +# For debug import time + import random from constraint import * @@ -14,8 +17,44 @@ from riscv_ctg.dsp_function import * INSTR_FORMAT = { - 'rformat': '$instr $rd, $rs1, $rs2', - 'iformat': '$instr $rd, $rs1, $imm_val' + 'rformat' : '$instr $rd, $rs1, $rs2', + 'iformat' : '$instr $rd, $rs1, $imm_val', + 'sformat' : '$instr $rs2, $imm_val($rs1)', + 'bsformat' : '$instr $rd, $rs2, $imm_val', + 'bformat' : '$instr $',### + 'uformat' : '$instr $rd, $imm_val', + 'jformat' : '$instr ', ### + 'crformat' : '$instr $rd, ', + 'cmvformat' : '$instr', + 'ciformat' : '$instr', + 'cssformat' : '$instr', + 'ciwformat' : '$instr', + 'clformat' : '$instr', + 'csformat' : '$instr', + 'caformat' : '$instr', + 'cbformat' : '$instr', + 'cjformat' : '$instr', + 'kformat' : '$instr', + 'frformat' : '$instr', + 'fsrformat' : '$instr', + 'fr4format' : '$instr', + 'pbrrformat' : '$instr', + 'phrrformat' : '$instr', + 'pbrformat' : '$instr', + 'phrformat' : '$instr', + 'pbriformat' : '$instr', + 'phriformat' : '$instr', + 'psbrrformat' : '$instr', + 'pshrrformat' : '$instr', + 'pwrrformat' : '$instr', + 'pwriformat' : '$instr', + 'pwrformat' : '$instr', + 'pswrrformat' : '$instr', + 'pwhrrformat' : '$instr', + 'pphrrformat' : '$instr', + 'ppbrrformat' : '$instr', + 'prrformat' : '$instr', + 'prrrformat' : '$instr' } REG_INIT = ''' @@ -54,6 +93,8 @@ #endif ''' +REG_INIT_TEMP = 'LI ($reg, ($val & MASK))' + class cross(): ''' A cross class to genereate RISC-V assembly tests for cross-combination coverpoints. @@ -356,6 +397,14 @@ def swreg(cross_comb_instrs): return sreg + def get_reginit_str(cross_comb_instrs): + + for instr_dict in cross_comb_instrs: + for key, val in instr_dict.items(): + if key != 'instr' and key != 'imm_val': + pass + + def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): code = '\n' From 33b245399250d8f47af27964b72d19de39be69fc Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Mon, 30 May 2022 22:47:53 +0530 Subject: [PATCH 25/50] Floating point support and limit initialization --- riscv_ctg/cross_comb.py | 108 ++++++++++++++++++++++++---------------- 1 file changed, 65 insertions(+), 43 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 9c3cca18..9ba79ca1 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -7,6 +7,7 @@ from constraint import * import riscv_isac.utils as isac_utils +from riscv_ctg import constants import riscv_ctg.utils as utils import riscv_ctg.constants as const @@ -57,41 +58,39 @@ 'prrrformat' : '$instr' } -REG_INIT = ''' -LI (x1, (0xFEEDBEADFEEDBEAD & MASK)); -LI (x2, (0xFF76DF56FF76DF56 & MASK)); -LI (x3, (0x7FBB6FAB7FBB6FAB & MASK)); -LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK)); -LA (x5, (0xAB7FFB6FAB7FBB6F & MASK)); -LA (x6, (0x6FAB71BB6F7B7FBB & MASK)); -LI (x7, (0xB7FBB6FAB7FBB6FA & MASK)); -LI (x8, (0x5BFDDB7D5BFDDB7D & MASK)); -LI (x9, (0xADFEEDBEADFEEDBE & MASK)); -LI (x10, (0x56FF76DF56FF76DF & MASK)); -LI (x11, (0xAB7FBB6FAB7FBB6F & MASK)); -LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK)); -LI (x13, (0xEADFEEDBEADFEEDB & MASK)); -LI (x14, (0xF56FF76DF56FF76D & MASK)); -LI (x15, (0xFAB7FBB6FAB7FBB6 & MASK)); -#ifndef RVTEST_E -LI (x16, (0x7D5BFDDB7D5BFDDB & MASK)); -LI (x17, (0xBEADFEEDBEADFEED & MASK)); -LI (x18, (0xDF56FF76DF56FF76 & MASK)); -LI (x19, (0x6FAB7FBB6FAB7FBB & MASK)); -LI (x20, (0xB7D5BFDDB7D5BFDD & MASK)); -LI (x21, (0xDBEADFEEDBEADFEE & MASK)); -LI (x22, (0x6DF56FF76DF56FF7 & MASK)); -LI (x23, (0xB6FAB7FBB6FAB7FB & MASK)); -LI (x24, (0xDB7D5BFDDB7D5BFD & MASK)); -LI (x25, (0xEDBEADFEEDBEADFE & MASK)); -LI (x26, (0x76DF56FF76DF56FF & MASK)); -LI (x27, (0xBB6FAB7FBB6FAB7F & MASK)); -LI (x28, (0xDDB7D5BFDDB7D5BF & MASK)); -LI (x29, (0xEEDBEADFEEDBEADF & MASK)); -LI (x30, (0xF76DF56FF76DF56F & MASK)); -LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK)); -#endif -''' +REG_INIT = { +'x1' : 'LI (x1, (0xFEEDBEADFEEDBEAD & MASK))', +'x2' : 'LI (x2, (0xFF76DF56FF76DF56 & MASK))', +'x3' : 'LI (x3, (0x7FBB6FAB7FBB6FAB & MASK))', +'x4' : 'LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK))', +'x5' : 'LI (x5, (0xAB7FFB6FAB7FBB6F & MASK))', +'x6' : 'LI (x6, (0x6FAB71BB6F7B7FBB & MASK))', +'x7' : 'LI (x7, (0xB7FBB6FAB7FBB6FA & MASK))', +'x8' : 'LI (x8, (0x5BFDDB7D5BFDDB7D & MASK))', +'x9' : 'LI (x9, (0xADFEEDBEADFEEDBE & MASK))', +'x10' : 'LI (x10, (0x56FF76DF56FF76DF & MASK))', +'x11' : 'LI (x11, (0xAB7FBB6FAB7FBB6F & MASK))', +'x12' : 'LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK))', +'x13' : 'LI (x13, (0xEADFEEDBEADFEEDB & MASK))', +'x14' : 'LI (x14, (0xF56FF76DF56FF76D & MASK))', +'x15' : 'LI (x15, (0xFAB7FBB6FAB7FBB6 & MASK))', +'x16' : 'LI (x16, (0x7D5BFDDB7D5BFDDB & MASK))', +'x17' : 'LI (x17, (0xBEADFEEDBEADFEED & MASK))', +'x18' : 'LI (x18, (0xDF56FF76DF56FF76 & MASK))', +'x19' : 'LI (x19, (0x6FAB7FBB6FAB7FBB & MASK))', +'x20' : 'LI (x20, (0xB7D5BFDDB7D5BFDD & MASK))', +'x21' : 'LI (x21, (0xDBEADFEEDBEADFEE & MASK))', +'x22' : 'LI (x22, (0x6DF56FF76DF56FF7 & MASK))', +'x23' : 'LI (x23, (0xB6FAB7FBB6FAB7FB & MASK))', +'x24' : 'LI (x24, (0xDB7D5BFDDB7D5BFD & MASK))', +'x25' : 'LI (x25, (0xEDBEADFEEDBEADFE & MASK))', +'x26' : 'LI (x26, (0x76DF56FF76DF56FF & MASK))', +'x27' : 'LI (x27, (0xBB6FAB7FBB6FAB7F & MASK))', +'x28' : 'LI (x28, (0xDDB7D5BFDDB7D5BF & MASK))', +'x29' : 'LI (x29, (0xEEDBEADFEEDBEADF & MASK))', +'x30' : 'LI (x30, (0xF76DF56FF76DF56F & MASK))', +'x31' : 'LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK))' +} REG_INIT_TEMP = 'LI ($reg, ($val & MASK))' @@ -113,6 +112,9 @@ def __init__(self, base_isa_str, xlen_in, randomize, label): self.randomize = randomize self.label = label + + # Flag if floating point instructions are chosen + self.if_fp = False def cross_comb(self, cgf_node): ''' @@ -275,6 +277,10 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) + # Set floating point flag if instruction belongs to F or D extension + if instr[0] == 'f' and instr != 'fence': + self.if_fp = True + opr_vals['instr'] = instr solution += [opr_vals] @@ -364,9 +370,14 @@ def exc_rd_zero(*oprs_lst): exec(each) isa_set += (cross.OP_TEMPLATE[instr]['isa']) + + # Set floating point flag if instruction belongs to F or D extension + if instr[0] == 'f' and instr != 'fence': + self.if_fp = True + opr_vals['instr'] = instr solution += [opr_vals] - + full_solution += [solution] self.isa = list(set(isa_set)) @@ -398,13 +409,17 @@ def swreg(cross_comb_instrs): return sreg def get_reginit_str(cross_comb_instrs): + + reg_init_lst = set() for instr_dict in cross_comb_instrs: for key, val in instr_dict.items(): - if key != 'instr' and key != 'imm_val': - pass + if key != 'rd': + if key != 'instr' and key != 'imm_val' and val != 'x0': + reg_init_lst.add(REG_INIT[val]) + + return list(reg_init_lst) - def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): code = '\n' @@ -413,6 +428,10 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): sig = [''] sreg_dict = dict() + # If floating point instructions are available + if self.if_fp: + code += "RVTEST_FP_ENABLE()" + # Generate ISA and extension string extension = "" rvxlen = "RV"+str(xlen) @@ -450,18 +469,21 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): code = code + instr_str + '\n' # Append .fill assembly directives to initialize signature regions - sig.append(sig_label + ':\n\t.fill ' + str(len(rd_lst)) + ', 4, 0xdeadbeef\n') + sig.append(signode_template.safe_substitute(label = sig_label, n = len(rd_lst))) offset = 0 code += '\n// Store destination register values in the test signature region\n' # Add signature update statement(s) for unique number of rds for rd in rd_lst: - sig_upd = f'RVTEST_SIGUPD({sreg}, {rd}, {offset})' + if rd[0] == 'f': + sig_upd = f'RVTEST_SIGUPD_F({sreg}, {rd}, {offset})' + else: + sig_upd = f'RVTEST_SIGUPD({sreg}, {rd}, {offset})' offset = offset + int(xlen/8) code = code + sig_upd + '\n' # Initialize registers for next cross-comb coverpoint - code = code + REG_INIT + code = code + '\n// Initialize used registers\n' + '\n'.join(cross.get_reginit_str(cross_sol)) + '\n' case_str = ''.join([case_template.safe_substitute(xlen = xlen,num = i, cond = cond, cov_label = cov_label) for i, cond in enumerate(cgf_node['config'])]) test = part_template.safe_substitute(case_str = case_str, code = code) @@ -489,7 +511,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): node = {'config': {'check ISA:=regex(.*I.*)'}, 'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW - '[add : ? : rv32i_shift : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW + '[fadd.s : ? : rv32i_shift : ? : fsub.d] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW '[(add,sub) : ? : mul : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR } From 525da0406e0343383cbede34383d7061f7a8485b Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 2 Jun 2022 09:54:23 +0530 Subject: [PATCH 26/50] Support for floating point instructions --- riscv_ctg/cross_comb.py | 72 ++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 22 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 9ba79ca1..76efd8bf 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -36,7 +36,7 @@ 'cbformat' : '$instr', 'cjformat' : '$instr', 'kformat' : '$instr', - 'frformat' : '$instr', + 'frformat' : '$instr $rd, $rs1, $rs2', 'fsrformat' : '$instr', 'fr4format' : '$instr', 'pbrrformat' : '$instr', @@ -59,15 +59,15 @@ } REG_INIT = { -'x1' : 'LI (x1, (0xFEEDBEADFEEDBEAD & MASK))', -'x2' : 'LI (x2, (0xFF76DF56FF76DF56 & MASK))', -'x3' : 'LI (x3, (0x7FBB6FAB7FBB6FAB & MASK))', -'x4' : 'LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK))', -'x5' : 'LI (x5, (0xAB7FFB6FAB7FBB6F & MASK))', -'x6' : 'LI (x6, (0x6FAB71BB6F7B7FBB & MASK))', -'x7' : 'LI (x7, (0xB7FBB6FAB7FBB6FA & MASK))', -'x8' : 'LI (x8, (0x5BFDDB7D5BFDDB7D & MASK))', -'x9' : 'LI (x9, (0xADFEEDBEADFEEDBE & MASK))', +'x1' : 'LI (x1, (0xFEEDBEADFEEDBEAD & MASK))', +'x2' : 'LI (x2, (0xFF76DF56FF76DF56 & MASK))', +'x3' : 'LI (x3, (0x7FBB6FAB7FBB6FAB & MASK))', +'x4' : 'LI (x4, (0xBFDDB7D5BFDDB7D5 & MASK))', +'x5' : 'LI (x5, (0xAB7FFB6FAB7FBB6F & MASK))', +'x6' : 'LI (x6, (0x6FAB71BB6F7B7FBB & MASK))', +'x7' : 'LI (x7, (0xB7FBB6FAB7FBB6FA & MASK))', +'x8' : 'LI (x8, (0x5BFDDB7D5BFDDB7D & MASK))', +'x9' : 'LI (x9, (0xADFEEDBEADFEEDBE & MASK))', 'x10' : 'LI (x10, (0x56FF76DF56FF76DF & MASK))', 'x11' : 'LI (x11, (0xAB7FBB6FAB7FBB6F & MASK))', 'x12' : 'LI (x12, (0xD5BFDDB7D5BFDDB7 & MASK))', @@ -89,10 +89,42 @@ 'x28' : 'LI (x28, (0xDDB7D5BFDDB7D5BF & MASK))', 'x29' : 'LI (x29, (0xEEDBEADFEEDBEADF & MASK))', 'x30' : 'LI (x30, (0xF76DF56FF76DF56F & MASK))', -'x31' : 'LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK))' +'x31' : 'LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK))', +'f0' : 'FLREG f0, 0xFEDDB7ADFEEDBE2D >> FREGWIDTH', +'f1' : 'FLREG f1, 0xFEEDBEADFEEDBEAD >> FREGWIDTH', +'f2' : 'FLREG f2, 0xFF76DF56FF76DF56 >> FREGWIDTH', +'f3' : 'FLREG f3, 0x7FBB6FAB7FBB6FAB >> FREGWIDTH', +'f4' : 'FLREG f4, 0xBFDDB7D5BFDDB7D5 >> FREGWIDTH', +'f5' : 'FLREG f5, 0xAB7FFB6FAB7FBB6F >> FREGWIDTH', +'f6' : 'FLREG f6, 0x6FAB71BB6F7B7FBB >> FREGWIDTH', +'f7' : 'FLREG f7, 0xB7FBB6FAB7FBB6FA >> FREGWIDTH', +'f8' : 'FLREG f8, 0x5BFDDB7D5BFDDB7D >> FREGWIDTH', +'f9' : 'FLREG f9, 0xADFEEDBEADFEEDBE >> FREGWIDTH', +'f10' : 'FLREG f10, 0x56FF76DF56FF76DF >> FREGWIDTH', +'f11' : 'FLREG f11, 0xAB7FBB6FAB7FBB6F >> FREGWIDTH', +'f12' : 'FLREG f12, 0xD5BFDDB7D5BFDDB7 >> FREGWIDTH', +'f13' : 'FLREG f13, 0xEADFEEDBEADFEEDB >> FREGWIDTH', +'f14' : 'FLREG f14, 0xF56FF76DF56FF76D >> FREGWIDTH', +'f15' : 'FLREG f15, 0xFAB7FBB6FAB7FBB6 >> FREGWIDTH', +'f16' : 'FLREG f16, 0x7D5BFDDB7D5BFDDB >> FREGWIDTH', +'f17' : 'FLREG f17, 0xBEADFEEDBEADFEED >> FREGWIDTH', +'f18' : 'FLREG f18, 0xDF56FF76DF56FF76 >> FREGWIDTH', +'f19' : 'FLREG f19, 0x6FAB7FBB6FAB7FBB >> FREGWIDTH', +'f20' : 'FLREG f20, 0xB7D5BFDDB7D5BFDD >> FREGWIDTH', +'f21' : 'FLREG f21, 0xDBEADFEEDBEADFEE >> FREGWIDTH', +'f22' : 'FLREG f22, 0x6DF56FF76DF56FF7 >> FREGWIDTH', +'f23' : 'FLREG f23, 0xB6FAB7FBB6FAB7FB >> FREGWIDTH', +'f24' : 'FLREG f24, 0xDB7D5BFDDB7D5BFD >> FREGWIDTH', +'f25' : 'FLREG f25, 0xEDBEADFEEDBEADFE >> FREGWIDTH', +'f26' : 'FLREG f26, 0x76DF56FF76DF56FF >> FREGWIDTH', +'f27' : 'FLREG f27, 0xBB6FAB7FBB6FAB7F >> FREGWIDTH', +'f28' : 'FLREG f28, 0xDDB7D5BFDDB7D5BF >> FREGWIDTH', +'f29' : 'FLREG f29, 0xEEDBEADFEEDBEADF >> FREGWIDTH', +'f30' : 'FLREG f30, 0xF76DF56FF76DF56F >> FREGWIDTH', +'f31' : 'FLREG f31, 0xFBB6FAB7FBB6FAB7 >> FREGWIDTH' } -REG_INIT_TEMP = 'LI ($reg, ($val & MASK))' +FREG_INIT_TEMP = Template('FLREG $freg, $val >> FREGWIDTH') class cross(): ''' @@ -390,22 +422,16 @@ def swreg(cross_comb_instrs): global base_isa - op_vals = ['x0'] + op_vals = {'x0'} for instr_dict in cross_comb_instrs: for key, val in instr_dict.items(): if key != 'instr' and key != 'imm_val': - op_vals += val + op_vals.add(val) - problem = Problem() - problem.addVariable('o', ['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)]) - problem.addConstraint(lambda op: op not in op_vals) - - swreg_sol = problem.getSolutions() - swreg_sol = [list(each.items())[0][1] for each in swreg_sol] - - sreg = random.choice(swreg_sol) + swreg_sol = set(['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)]) - op_vals + sreg = random.choice(list(swreg_sol)) return sreg def get_reginit_str(cross_comb_instrs): @@ -422,6 +448,8 @@ def get_reginit_str(cross_comb_instrs): def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): + global base_isa + code = '\n' data = [".align 4","rvtest_data:",".word 0xbabecafe", \ ".word 0xabecafeb", ".word 0xbecafeba", ".word 0xecafebab"] From 7cacdf7f201ec9fefcecd0e196aa1b1bca481a5c Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 2 Jun 2022 10:05:04 +0530 Subject: [PATCH 27/50] Correction to register initialization --- riscv_ctg/cross_comb.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 76efd8bf..5d69b523 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -439,10 +439,7 @@ def get_reginit_str(cross_comb_instrs): reg_init_lst = set() for instr_dict in cross_comb_instrs: - for key, val in instr_dict.items(): - if key != 'rd': - if key != 'instr' and key != 'imm_val' and val != 'x0': - reg_init_lst.add(REG_INIT[val]) + reg_init_lst.add(REG_INIT[instr_dict['rd']]) return list(reg_init_lst) From a348f52411c949e4627981a636304a3e92252da7 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 05:04:34 +0530 Subject: [PATCH 28/50] Remove Instruction Alias --- riscv_ctg/cross_comb.py | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 5d69b523..d4a2ad02 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -19,12 +19,12 @@ INSTR_FORMAT = { 'rformat' : '$instr $rd, $rs1, $rs2', - 'iformat' : '$instr $rd, $rs1, $imm_val', + 'iformat' : '$instr $rd, $rs1, SEXT_IMM($imm_val)', 'sformat' : '$instr $rs2, $imm_val($rs1)', 'bsformat' : '$instr $rd, $rs2, $imm_val', - 'bformat' : '$instr $',### + 'bformat' : '$instr $rs1, $rs2, $label', 'uformat' : '$instr $rd, $imm_val', - 'jformat' : '$instr ', ### + 'jformat' : '$instr ', 'crformat' : '$instr $rd, ', 'cmvformat' : '$instr', 'ciformat' : '$instr', @@ -124,8 +124,6 @@ 'f31' : 'FLREG f31, 0xFBB6FAB7FBB6FAB7 >> FREGWIDTH' } -FREG_INIT_TEMP = Template('FLREG $freg, $val >> FREGWIDTH') - class cross(): ''' A cross class to genereate RISC-V assembly tests for cross-combination coverpoints. @@ -320,7 +318,8 @@ def exc_rd_zero(*oprs_lst): # When instruction(s)/alias is specified, # - If an instruction is specified, operands are directly extracted and assigned values according to conditions # - If a tuple of instructions is specified, one of the instruction is chosen at random - # - If an alias is specified, the instruction is chosen according to assignment and condition list + # - If an alias is specified, the alias is already substituted by its equivalent tuple of instructions at this point + # through expand_cgf method. # - Immediate values are generated if required # - Assignments are evaluated cond = cond_lst[i] @@ -334,20 +333,8 @@ def exc_rd_zero(*oprs_lst): if data[i] in cross.OP_TEMPLATE: # If single instruction instr = data[i] - else: - alias_instrs = isac_utils.import_instr_alias(data[i]) # If data is an alias - if alias_instrs: - problem.reset() - problem.addVariable('i', alias_instrs) - problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) - instrs_sol = problem.getSolutions() - - instrs_sol = [list(each.items())[0][1] for each in instrs_sol] - - # Randomly select an instruction - instr = random.choice(instrs_sol) - - elif data[i].find('(') != -1: # If data is a tuple of instructions + else: + if data[i].find('(') != -1: # If data is a tuple of instructions instrs_sol = data[i][1:-1].split(',') instr = random.choice(instrs_sol) else: @@ -435,6 +422,17 @@ def swreg(cross_comb_instrs): return sreg def get_reginit_str(cross_comb_instrs): + ''' + This function fetches the register initlialization macro to initialize + used destination instructions after the cross-coverpoint instruction sequence + generation + + Input argument: + - cross_comb_instrs type: list(dict()) Holds info of various instructions in the sequence + + Return: + - List of initialization strings + ''' reg_init_lst = set() @@ -479,7 +477,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): sig_label = "signature_" + sreg + "_" + str(sreg_dict[sreg]) code = code + "\nRVTEST_SIGBASE(" + sreg + ", "+ sig_label + ")\n\n" - + rd_lst = set() # Generate instruction corresponding to each instruction dictionary # Append signature update statements to store rd value after each instruction @@ -537,7 +535,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): 'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW '[fadd.s : ? : rv32i_shift : ? : fsub.d] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW - '[(add,sub) : ? : mul : ? : (add,sub)] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]': 0, # WAW + '[(add,sub) : ? : mul : ? : rv32i_shift : (add,sub)] :: [a=rd : ? : b = rs2 : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==b : rd==a]': 0, # WAW '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR } } From b7848458a47b2dae36fa049c956b6270d6af5b8d Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 05:05:08 +0530 Subject: [PATCH 29/50] Documentation --- docs/source/cross_comb.rst | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 docs/source/cross_comb.rst diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst new file mode 100644 index 00000000..1d55c70e --- /dev/null +++ b/docs/source/cross_comb.rst @@ -0,0 +1,19 @@ +************************************************ +Test Generation using Cross Coverage Coverpoints +************************************************ + +Coverpoints constituting multiple instructions can help identify interesting instruction +sequences which have architectural significance such as structural hazards and data hazards. +The coverpoint node associated with the test generation is ``cross_comb`` defined here. + +The test generator employs a constraint solver to generate relevant instruction sequence for a +``cross_comb`` coverpoint. + +Example +####### + +Consider a cross combination coverpoint defined as: + +code :: + + From 3b115800aac82fb8bd63edf7d7e83ce10808e285 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 00:12:11 +0530 Subject: [PATCH 30/50] Formatting --- docs/source/cross_comb.rst | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 1d55c70e..153cd2d6 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -14,6 +14,20 @@ Example Consider a cross combination coverpoint defined as: -code :: +Coverpoint Definition +##################### +:: + add: + cross_comb: + "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" +Possible assembly sequence generated +#################################### + +:: + add x3, x3, x4; + addi x5, x3, 1; + sub x6, x4, x3; + addi x4, x3, -3; + sub x3, x5, x6; From 836099a44b39651a94f619c02a4e9dbf70f3d972 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 00:16:32 +0530 Subject: [PATCH 31/50] Formatting --- docs/source/cross_comb.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 153cd2d6..2050d441 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -17,7 +17,7 @@ Consider a cross combination coverpoint defined as: Coverpoint Definition ##################### -:: +.. code-block:: add: cross_comb: "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" @@ -25,7 +25,7 @@ Coverpoint Definition Possible assembly sequence generated #################################### -:: +.. code-block:: add x3, x3, x4; addi x5, x3, 1; sub x6, x4, x3; From fe5373db5769130c4dc27c9f46de1b292ef8f2a2 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 09:12:12 +0530 Subject: [PATCH 32/50] Format --- docs/source/cross_comb.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 2050d441..b9027e32 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -10,12 +10,14 @@ The test generator employs a constraint solver to generate relevant instruction ``cross_comb`` coverpoint. Example -####### +------- Consider a cross combination coverpoint defined as: Coverpoint Definition -##################### +--------------------- + +An example cross combination coverpoint is given below: .. code-block:: add: @@ -25,6 +27,8 @@ Coverpoint Definition Possible assembly sequence generated #################################### +A possible sequence of instructions CTG would generate is: + .. code-block:: add x3, x3, x4; addi x5, x3, 1; From bf20eadcaaaaeae34d799e4a42bc126fa5032809 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 09:14:28 +0530 Subject: [PATCH 33/50] Format --- docs/source/cross_comb.rst | 40 +++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index b9027e32..c44f1ba4 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -12,26 +12,22 @@ The test generator employs a constraint solver to generate relevant instruction Example ------- -Consider a cross combination coverpoint defined as: - -Coverpoint Definition ---------------------- - -An example cross combination coverpoint is given below: - -.. code-block:: - add: - cross_comb: - "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" + **Coverpoint Definition** -Possible assembly sequence generated -#################################### - -A possible sequence of instructions CTG would generate is: - -.. code-block:: - add x3, x3, x4; - addi x5, x3, 1; - sub x6, x4, x3; - addi x4, x3, -3; - sub x3, x5, x6; + An example cross combination coverpoint is given below: + + .. code-block:: + add: + cross_comb: + "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" + + **Possible assembly sequence generated** + + A possible sequence of instructions CTG would generate is: + + .. code-block:: + add x3, x3, x4; + addi x5, x3, 1; + sub x6, x4, x3; + addi x4, x3, -3; + sub x3, x5, x6; From 94982f49dc0fa41ec83545123431705a25feecec Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 09:15:56 +0530 Subject: [PATCH 34/50] Code block formatting --- docs/source/cross_comb.rst | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index c44f1ba4..3e8ef981 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -13,19 +13,17 @@ Example ------- **Coverpoint Definition** - - An example cross combination coverpoint is given below: - - .. code-block:: + + An example cross combination coverpoint is given below: :: + add: cross_comb: "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" - + **Possible assembly sequence generated** + + A possible sequence of instructions CTG would generate is: :: - A possible sequence of instructions CTG would generate is: - - .. code-block:: add x3, x3, x4; addi x5, x3, 1; sub x6, x4, x3; From 1de5afa0c555be11172d7a03e23e12c7a6c5f2c2 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 09:57:24 +0530 Subject: [PATCH 35/50] Code cleanup --- riscv_ctg/cross_comb.py | 94 +++++++++++++---------------------------- 1 file changed, 30 insertions(+), 64 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index d4a2ad02..cfcf1c01 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -1,8 +1,4 @@ # See LICENSE.incore for details - -# For debug -import time - import random from constraint import * @@ -24,39 +20,40 @@ 'bsformat' : '$instr $rd, $rs2, $imm_val', 'bformat' : '$instr $rs1, $rs2, $label', 'uformat' : '$instr $rd, $imm_val', - 'jformat' : '$instr ', - 'crformat' : '$instr $rd, ', - 'cmvformat' : '$instr', - 'ciformat' : '$instr', - 'cssformat' : '$instr', - 'ciwformat' : '$instr', - 'clformat' : '$instr', - 'csformat' : '$instr', + 'jformat' : '$instr $rd, $imm', + 'crformat' : '$instr $rs1 $rs2', + 'cmvformat' : '$instr $rd, $rs2', + 'ciformat' : '$instr $rd, $imm_val', + 'cssformat' : '$instr $rs2, $imm_val($rs1)', + 'ciwformat' : '$instr $rd, x2, $imm_val', + 'clformat' : '$instr $rd, $imm_val($rs1)', + 'csformat' : '$instr $rs2, $imm_val($rs2)', 'caformat' : '$instr', 'cbformat' : '$instr', 'cjformat' : '$instr', 'kformat' : '$instr', 'frformat' : '$instr $rd, $rs1, $rs2', - 'fsrformat' : '$instr', - 'fr4format' : '$instr', - 'pbrrformat' : '$instr', - 'phrrformat' : '$instr', - 'pbrformat' : '$instr', - 'phrformat' : '$instr', - 'pbriformat' : '$instr', - 'phriformat' : '$instr', - 'psbrrformat' : '$instr', - 'pshrrformat' : '$instr', - 'pwrrformat' : '$instr', - 'pwriformat' : '$instr', - 'pwrformat' : '$instr', - 'pswrrformat' : '$instr', - 'pwhrrformat' : '$instr', - 'pphrrformat' : '$instr', - 'ppbrrformat' : '$instr', - 'prrformat' : '$instr', + 'fsrformat' : '$instr $rd, $rs1', + 'fr4format' : '$instr $rd, $rs1, $rs2, $rs3', + 'pbrrformat' : '$instr $rd, $rs1, $rs2', + 'phrrformat' : '$instr $rd, $rs1, $rs2', + 'pbrformat' : '$instr $rd, $rs1', + 'phrformat' : '$instr $rd, $rs1', + 'pbriformat' : '$instr $rd, $rs1, SEXT_IMM($imm_val)' , + 'phriformat' : '$instr $rd, $rs1, SEXT_IMM($imm_val)', + 'psbrrformat' : '$instr $rd, $rs1, $rs2', + 'pshrrformat' : '$instr $rd, $rs1, $rs2', + 'pwrrformat' : '$instr $rd, $rs1, $rs2', + 'pwriformat' : '$instr $rd, $rs1, SEXT_IMM($imm_val)' , + 'pwrformat' : '$instr $rd, $rs1', + 'pswrrformat' : '$instr $rd, $rs1, $rs2', + 'pwhrrformat' : '$instr $rd, $rs1, $rs2', + 'pphrrformat' : '$instr $rd, $rs1, $rs2', + 'ppbrrformat' : '$instr $rd, $rs1, $rs2', + 'prrformat' : '$instr ', 'prrrformat' : '$instr' } +'''Dictionary to store instruction formats''' REG_INIT = { 'x1' : 'LI (x1, (0xFEEDBEADFEEDBEAD & MASK))', @@ -123,6 +120,7 @@ 'f30' : 'FLREG f30, 0xF76DF56FF76DF56F >> FREGWIDTH', 'f31' : 'FLREG f31, 0xFBB6FAB7FBB6FAB7 >> FREGWIDTH' } +''' Initial values for general purpose and floating point registers''' class cross(): ''' @@ -205,10 +203,7 @@ def eval_conds(*oprs_lst): cond_lst = parts[2].lstrip().rstrip()[1:-1].split(':') # Initialize CSP - if self.randomize: - problem = Problem(MinConflictsSolver) - else: - problem = Problem() + problem = Problem() for i in range(len(data)): if data[i] == '?': @@ -521,33 +516,4 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): label = cov_label, extension = extension ) - ) - -if __name__ == '__main__': - - cov_node = 'add' - isa = 'RV32I' - cgf_arg = '/path/to/cgf/' - rand_arg = True - xlen = 32 - fprefix = '.' - node = {'config': {'check ISA:=regex(.*I.*)'}, - 'cross_comb' : {'[(add,sub) : (add,sub) ] :: [a=rd : ? ] :: [? : rs1==a or rs2==a]' : 0, # RAW - '[(add,sub) : ? : (add,sub) ] :: [a=rd : ? : ? ] :: [rd==x10 : rd!=a and rs1!=a and rs2!=a : rs1==a or rs2==a ]': 0, # RAW - '[fadd.s : ? : rv32i_shift : ? : fsub.d] :: [a=rd : ? : ? : ? : ?] :: [? : ? : ? : ? : rd==a]': 0, # WAW - '[(add,sub) : ? : mul : ? : rv32i_shift : (add,sub)] :: [a=rd : ? : b = rs2 : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==b : rd==a]': 0, # WAW - '[(add,sub) : (add,sub) ] :: [a=rs1; b=rs2 : ? ] :: [? : rd==a or rd==b]': 0 # WAR - } - } - - mytime = time.asctime(time.gmtime(time.time()) ) + ' GMT' - usage_str = const.usage.safe_substitute(version = __version__, - time = mytime, - xlen = xlen, - cgf = cgf_arg, - randomize = rand_arg - ) - - cross_test = cross('rv32i', 32, False, cov_node) - full_solution = cross_test.cross_comb(node) - cross_test.write_test(fprefix, node, usage_str, cov_node, full_solution) \ No newline at end of file + ) \ No newline at end of file From 5eb7b69228932bc34cc4b0e47701915df89b3b24 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 10:05:36 +0530 Subject: [PATCH 36/50] Link --- docs/source/cross_comb.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 3e8ef981..bd12bece 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -4,7 +4,7 @@ Test Generation using Cross Coverage Coverpoints Coverpoints constituting multiple instructions can help identify interesting instruction sequences which have architectural significance such as structural hazards and data hazards. -The coverpoint node associated with the test generation is ``cross_comb`` defined here. +The coverpoint node associated with the test generation is ``cross_comb`` defined `here `_. The test generator employs a constraint solver to generate relevant instruction sequence for a ``cross_comb`` coverpoint. From 62d479babd91e8254d3013b8dac85bcda22bebaa Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 10:08:01 +0530 Subject: [PATCH 37/50] Formatting --- docs/source/cross_comb.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index bd12bece..5656aa0c 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -20,12 +20,12 @@ Example cross_comb: "[add : ? : rv32i_arith : ? : sub] :: [a=rd : ? : ? : ? : ?] :: [? : rs1==a or rs2==a : rs1==a or rs2==a : rs1==a or rs2==a : rd==a]" - **Possible assembly sequence generated** + **Possible assembly sequence** A possible sequence of instructions CTG would generate is: :: - add x3, x3, x4; - addi x5, x3, 1; - sub x6, x4, x3; - addi x4, x3, -3; - sub x3, x5, x6; + add x3, x3, x4 + addi x5, x3, 1 + sub x6, x4, x3 + addi x4, x3, -3 + sub x3, x5, x6 From 29fdc0d36bdf6458d6905cbb4ad40f44f8218f95 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 10:10:51 +0530 Subject: [PATCH 38/50] More info --- docs/source/cross_comb.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 5656aa0c..807df152 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -29,3 +29,5 @@ Example sub x6, x4, x3 addi x4, x3, -3 sub x3, x5, x6 + +The test generator also embeds appropriate macros for initialization of registers and signature region pointing registers. \ No newline at end of file From 2273cdfb3d431f0c06330a756f81a53e0c9dff27 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 13:36:35 +0530 Subject: [PATCH 39/50] Report invalid instruction error appropriately --- riscv_ctg/cross_comb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index cfcf1c01..25495e47 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -333,7 +333,7 @@ def exc_rd_zero(*oprs_lst): instrs_sol = data[i][1:-1].split(',') instr = random.choice(instrs_sol) else: - logger.error('Invalid instruction/alias in cross_comb: ' + each) + logger.error('Invalid instruction/alias in cross_comb: ' + data[i]) # Gather operands formattype = cross.OP_TEMPLATE[instr]['formattype'] From 36396271209d7ac5924bf1065fa55790c5faab2f Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 13:57:57 +0530 Subject: [PATCH 40/50] Floating point instruction rd initialization --- riscv_ctg/cross_comb.py | 54 ++++++++++++----------------------------- 1 file changed, 15 insertions(+), 39 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 25495e47..afab00a7 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -87,38 +87,6 @@ 'x29' : 'LI (x29, (0xEEDBEADFEEDBEADF & MASK))', 'x30' : 'LI (x30, (0xF76DF56FF76DF56F & MASK))', 'x31' : 'LI (x31, (0xFBB6FAB7FBB6FAB7 & MASK))', -'f0' : 'FLREG f0, 0xFEDDB7ADFEEDBE2D >> FREGWIDTH', -'f1' : 'FLREG f1, 0xFEEDBEADFEEDBEAD >> FREGWIDTH', -'f2' : 'FLREG f2, 0xFF76DF56FF76DF56 >> FREGWIDTH', -'f3' : 'FLREG f3, 0x7FBB6FAB7FBB6FAB >> FREGWIDTH', -'f4' : 'FLREG f4, 0xBFDDB7D5BFDDB7D5 >> FREGWIDTH', -'f5' : 'FLREG f5, 0xAB7FFB6FAB7FBB6F >> FREGWIDTH', -'f6' : 'FLREG f6, 0x6FAB71BB6F7B7FBB >> FREGWIDTH', -'f7' : 'FLREG f7, 0xB7FBB6FAB7FBB6FA >> FREGWIDTH', -'f8' : 'FLREG f8, 0x5BFDDB7D5BFDDB7D >> FREGWIDTH', -'f9' : 'FLREG f9, 0xADFEEDBEADFEEDBE >> FREGWIDTH', -'f10' : 'FLREG f10, 0x56FF76DF56FF76DF >> FREGWIDTH', -'f11' : 'FLREG f11, 0xAB7FBB6FAB7FBB6F >> FREGWIDTH', -'f12' : 'FLREG f12, 0xD5BFDDB7D5BFDDB7 >> FREGWIDTH', -'f13' : 'FLREG f13, 0xEADFEEDBEADFEEDB >> FREGWIDTH', -'f14' : 'FLREG f14, 0xF56FF76DF56FF76D >> FREGWIDTH', -'f15' : 'FLREG f15, 0xFAB7FBB6FAB7FBB6 >> FREGWIDTH', -'f16' : 'FLREG f16, 0x7D5BFDDB7D5BFDDB >> FREGWIDTH', -'f17' : 'FLREG f17, 0xBEADFEEDBEADFEED >> FREGWIDTH', -'f18' : 'FLREG f18, 0xDF56FF76DF56FF76 >> FREGWIDTH', -'f19' : 'FLREG f19, 0x6FAB7FBB6FAB7FBB >> FREGWIDTH', -'f20' : 'FLREG f20, 0xB7D5BFDDB7D5BFDD >> FREGWIDTH', -'f21' : 'FLREG f21, 0xDBEADFEEDBEADFEE >> FREGWIDTH', -'f22' : 'FLREG f22, 0x6DF56FF76DF56FF7 >> FREGWIDTH', -'f23' : 'FLREG f23, 0xB6FAB7FBB6FAB7FB >> FREGWIDTH', -'f24' : 'FLREG f24, 0xDB7D5BFDDB7D5BFD >> FREGWIDTH', -'f25' : 'FLREG f25, 0xEDBEADFEEDBEADFE >> FREGWIDTH', -'f26' : 'FLREG f26, 0x76DF56FF76DF56FF >> FREGWIDTH', -'f27' : 'FLREG f27, 0xBB6FAB7FBB6FAB7F >> FREGWIDTH', -'f28' : 'FLREG f28, 0xDDB7D5BFDDB7D5BF >> FREGWIDTH', -'f29' : 'FLREG f29, 0xEEDBEADFEEDBEADF >> FREGWIDTH', -'f30' : 'FLREG f30, 0xF76DF56FF76DF56F >> FREGWIDTH', -'f31' : 'FLREG f31, 0xFBB6FAB7FBB6FAB7 >> FREGWIDTH' } ''' Initial values for general purpose and floating point registers''' @@ -399,7 +367,8 @@ def exc_rd_zero(*oprs_lst): def swreg(cross_comb_instrs): ''' - This function generates the register which can be used as a signature pointer for each instruction + This function generates the register which can be used as a signature pointer for each instruction. + It also generates the register which stores the value used by floating point registers ''' global base_isa @@ -414,9 +383,11 @@ def swreg(cross_comb_instrs): swreg_sol = set(['x'+str(x) for x in range(0,32 if 'e' not in base_isa else 16)]) - op_vals sreg = random.choice(list(swreg_sol)) - return sreg + freg_Sol = swreg_sol - set(sreg) + freg = random.choice(list(freg_Sol)) + return (sreg, freg) - def get_reginit_str(cross_comb_instrs): + def get_reginit_str(cross_comb_instrs, freg): ''' This function fetches the register initlialization macro to initialize used destination instructions after the cross-coverpoint instruction sequence @@ -432,8 +403,13 @@ def get_reginit_str(cross_comb_instrs): reg_init_lst = set() for instr_dict in cross_comb_instrs: - reg_init_lst.add(REG_INIT[instr_dict['rd']]) - + if 'rd' in instr_dict: + rd_val = instr_dict['rd'] + if rd_val[0] == 'f': + freg_init = REG_INIT[freg].replace('& MASK', '>> FREGWIDTH') + reg_init_lst.add(freg_init + '\n' + 'FLREG ' + rd_val + ', 0(' + freg + ')') + else: + reg_init_lst.add(REG_INIT[instr_dict['rd']]) return list(reg_init_lst) def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): @@ -461,7 +437,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): for cross_sol in full_solution: # Designate signature update register - sreg = cross.swreg(cross_sol) + (sreg, freg) = cross.swreg(cross_sol) # Designate count of sreg for signature label generation if sreg not in sreg_dict: @@ -501,7 +477,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): code = code + sig_upd + '\n' # Initialize registers for next cross-comb coverpoint - code = code + '\n// Initialize used registers\n' + '\n'.join(cross.get_reginit_str(cross_sol)) + '\n' + code = code + '\n// Initialize used registers\n' + '\n'.join(cross.get_reginit_str(cross_sol, freg)) + '\n' case_str = ''.join([case_template.safe_substitute(xlen = xlen,num = i, cond = cond, cov_label = cov_label) for i, cond in enumerate(cgf_node['config'])]) test = part_template.safe_substitute(case_str = case_str, code = code) From 1fc8e8ca24e64f1919e04f6fabcb718e1dd98efe Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 14:01:06 +0530 Subject: [PATCH 41/50] Note about load/store/branch --- docs/source/cross_comb.rst | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/source/cross_comb.rst b/docs/source/cross_comb.rst index 807df152..b876e4ef 100644 --- a/docs/source/cross_comb.rst +++ b/docs/source/cross_comb.rst @@ -30,4 +30,6 @@ Example addi x4, x3, -3 sub x3, x5, x6 -The test generator also embeds appropriate macros for initialization of registers and signature region pointing registers. \ No newline at end of file +The test generator also embeds appropriate macros for initialization of registers and signature region pointing registers. + +Note: The cross-combination test generator as of now, does not support load, store and branch instructions \ No newline at end of file From 7bf67f2a5556770c3b6493673da82387ceccd8bf Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 14:09:34 +0530 Subject: [PATCH 42/50] File path correction --- riscv_ctg/ctg.py | 1 + 1 file changed, 1 insertion(+) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index eb83e3f7..e50882be 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -87,6 +87,7 @@ def gen_test(op_node, opcode): fprefix = os.path.join(out_dir,str(label)) cross_obj = cross(base_isa, xlen, randomize, label) cross_instr_dict = cross_obj.cross_comb(node) + logger.info('Writing cross-comb test') cross_obj.write_test(fprefix, node, usage_str, label, cross_instr_dict) # Return if there is no corresponding template From 79a444f8986852b82e3e38004cd5f4550071d65a Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 14:09:46 +0530 Subject: [PATCH 43/50] File path correction --- riscv_ctg/cross_comb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index afab00a7..25a347b3 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -483,7 +483,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): test = part_template.safe_substitute(case_str = case_str, code = code) # Write test to file - with open(fprefix + f'/{cov_label}_cross-comb.S', 'w') as fp: + with open(fprefix + '_cross-comb.S', 'w') as fp: fp.write(usage_str + const.cross_test_template.safe_substitute(opcode = cov_label, isa = op_node_isa, test = test, From 158d536cd8a641139716e69cb69473e39cc6a18e Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Tue, 7 Jun 2022 16:04:09 +0530 Subject: [PATCH 44/50] Comments and TODOs --- riscv_ctg/cross_comb.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 25a347b3..80675a45 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -413,7 +413,12 @@ def get_reginit_str(cross_comb_instrs, freg): return list(reg_init_lst) def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): - + ''' + Generate instruction sequence and write them into an assembly file + + #TODO Initialization of floating point registers + #TODO Handling loads/stores and branches in the sequence + ''' global base_isa code = '\n' From 7637484181d57ba32957fcf93a426f264ce6ffc1 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 25 Aug 2022 11:05:25 +0530 Subject: [PATCH 45/50] Load mutilple template files for cross --- riscv_ctg/cross_comb.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 80675a45..49efe2e2 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -94,15 +94,15 @@ class cross(): ''' A cross class to genereate RISC-V assembly tests for cross-combination coverpoints. ''' - - # Template dictionary - OP_TEMPLATE = utils.load_yaml(const.template_file) def __init__(self, base_isa_str, xlen_in, randomize, label): global xlen global flen global base_isa + # Template dictionary + OP_TEMPLATE = utils.load_yamls(const.template_files) + xlen = xlen_in base_isa = base_isa_str From 0acc2ceceaf310a5dca57e84980cfcbad3b14a48 Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 25 Aug 2022 12:24:41 +0530 Subject: [PATCH 46/50] Template files integration to cross_comb.py --- riscv_ctg/cross_comb.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index 49efe2e2..cb92a3a4 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -193,7 +193,7 @@ def eval_conds(*oprs_lst): # Get possible instructions based on the operand list problem.reset() problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + problem.addConstraint(lambda i: all(item in OPS[self.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() instrs_sol = [list(each.items())[0][1] for each in instrs_sol] @@ -214,19 +214,19 @@ def eval_conds(*oprs_lst): # Get possible instructions problem.reset() problem.addVariable('i', dntcare_instrs) - problem.addConstraint(lambda i: all(item in OPS[cross.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) + problem.addConstraint(lambda i: all(item in OPS[self.OP_TEMPLATE[i]['formattype']] for item in opr_lst)) instrs_sol = problem.getSolutions() instrs_sol = [list(each.items())[0][1] for each in instrs_sol] # Randomly choose an instruction instr = random.choice(instrs_sol) - isa_set += (cross.OP_TEMPLATE[instr]['isa']) + isa_set += (self.OP_TEMPLATE[instr]['isa']) # Choose operand values - formattype = cross.OP_TEMPLATE[instr]['formattype'] + formattype = self.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] - instr_template = cross.OP_TEMPLATE[instr] + instr_template = self.OP_TEMPLATE[instr] # Choose register values problem.reset() @@ -294,7 +294,7 @@ def exc_rd_zero(*oprs_lst): opr_lst = list(set(opr_lst)) - if data[i] in cross.OP_TEMPLATE: # If single instruction + if data[i] in self.OP_TEMPLATE: # If single instruction instr = data[i] else: if data[i].find('(') != -1: # If data is a tuple of instructions @@ -304,9 +304,9 @@ def exc_rd_zero(*oprs_lst): logger.error('Invalid instruction/alias in cross_comb: ' + data[i]) # Gather operands - formattype = cross.OP_TEMPLATE[instr]['formattype'] + formattype = self.OP_TEMPLATE[instr]['formattype'] oprs = OPS[formattype] - instr_template = cross.OP_TEMPLATE[instr] + instr_template = self.OP_TEMPLATE[instr] problem.reset() for opr in oprs: @@ -351,7 +351,7 @@ def exc_rd_zero(*oprs_lst): for each in assgns: exec(each) - isa_set += (cross.OP_TEMPLATE[instr]['isa']) + isa_set += (self.OP_TEMPLATE[instr]['isa']) # Set floating point flag if instruction belongs to F or D extension if instr[0] == 'f' and instr != 'fence': @@ -463,7 +463,7 @@ def write_test(self, fprefix, cgf_node, usage_str, cov_label, full_solution): if 'rd' in each: rd_lst.add(each['rd']) - instr_str_format = Template(INSTR_FORMAT[cross.OP_TEMPLATE[each['instr']]['formattype']]) + instr_str_format = Template(INSTR_FORMAT[self.OP_TEMPLATE[each['instr']]['formattype']]) instr_str = instr_str_format.substitute(each) code = code + instr_str + '\n' From 1400bad541e895933bd411ab81ea76776d341ffa Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 25 Aug 2022 12:32:30 +0530 Subject: [PATCH 47/50] Bug fixes --- riscv_ctg/cross_comb.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/riscv_ctg/cross_comb.py b/riscv_ctg/cross_comb.py index cb92a3a4..2b8172f9 100644 --- a/riscv_ctg/cross_comb.py +++ b/riscv_ctg/cross_comb.py @@ -101,7 +101,7 @@ def __init__(self, base_isa_str, xlen_in, randomize, label): global base_isa # Template dictionary - OP_TEMPLATE = utils.load_yamls(const.template_files) + self.OP_TEMPLATE = utils.load_yamls(const.template_files) xlen = xlen_in base_isa = base_isa_str From a88a7d9dd671acf7e9dddd5e720cfb24d89bf2e0 Mon Sep 17 00:00:00 2001 From: Edwin Joy <43539365+edwin7026@users.noreply.github.com> Date: Thu, 25 Aug 2022 12:53:51 +0530 Subject: [PATCH 48/50] Update CHANGELOG.md --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 183bd568..bb261be3 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.9.0] - 2022-08-25 +- Added support for cross_comb coverpoint test generation + ## [0.8.0] - 2022-08-08 - Added support for a distributed template database. - Added generic mechanisms to generate data sections based on test instances. From 3fe1582482c316888981bd05d5912c4ec07d5f8e Mon Sep 17 00:00:00 2001 From: Edwin Joy Date: Thu, 25 Aug 2022 12:57:03 +0530 Subject: [PATCH 49/50] =?UTF-8?q?Bump=20version:=200.8.0=20=E2=86=92=200.9?= =?UTF-8?q?.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- riscv_ctg/__init__.py | 2 +- setup.cfg | 2 +- setup.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/riscv_ctg/__init__.py b/riscv_ctg/__init__.py index c8e86302..74963953 100644 --- a/riscv_ctg/__init__.py +++ b/riscv_ctg/__init__.py @@ -4,5 +4,5 @@ __author__ = """InCore Semiconductors Pvt Ltd""" __email__ = 'incorebot@gmail.com' -__version__ = '0.8.0' +__version__ = '0.9.0' diff --git a/setup.cfg b/setup.cfg index 4e51c62d..2f0a64df 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,5 +1,5 @@ [bumpversion] -current_version = 0.8.0 +current_version = 0.9.0 commit = True tag = True diff --git a/setup.py b/setup.py index e9161e37..a83181a1 100644 --- a/setup.py +++ b/setup.py @@ -26,7 +26,7 @@ def read_requires(): setup( name='riscv_ctg', - version='0.8.0', + version='0.9.0', description="RISC-V CTG", long_description=readme + '\n\n', classifiers=[ From 7668daa86fcf18faa508804de0d9f52530f5a7cc Mon Sep 17 00:00:00 2001 From: S Pawan Kumar Date: Mon, 29 Aug 2022 09:53:28 +0530 Subject: [PATCH 50/50] Fixed hardcoded flen value bug. --- riscv_ctg/ctg.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/riscv_ctg/ctg.py b/riscv_ctg/ctg.py index cdf570d1..500a6d79 100644 --- a/riscv_ctg/ctg.py +++ b/riscv_ctg/ctg.py @@ -31,7 +31,6 @@ def gen_test(op_node, opcode): if xlen not in op_node['xlen']: logger.warning("Skipping {0} since its not supported in current XLEN:".format(opcode)) return - flen = 0 if 'flen' in op_node: if flen not in op_node['flen']: logger.warning("Skipping {0} since its not supported in current FLEN({1}):".format(\ @@ -79,15 +78,15 @@ def gen_test(op_node, opcode): else: logger.warning(str(opcode) + " not found in template file. Skipping") return - + if 'cross_comb' in node: fprefix = os.path.join(out_dir,str(label)) cross_obj = cross(base_isa, xlen, randomize, label) cross_instr_dict = cross_obj.cross_comb(node) logger.info('Writing cross-comb test') cross_obj.write_test(fprefix, node, usage_str, label, cross_instr_dict) - - # Return if there is no corresponding template + + # Return if there is no corresponding template if op_node is None: logger.warning("Skipping :" + str(opcode)) @@ -119,11 +118,6 @@ def ctg(verbose, out, random ,xlen_arg,flen_arg, cgf_file,num_procs,base_isa, ma op_template = utils.load_yamls(const.template_files) cgf = expand_cgf(cgf_file,xlen,flen) pool = mp.Pool(num_procs) -# <<<<<<< HEAD - # results = pool.starmap(create_test, [(usage_str, node,label,base_isa,max_inst) for label,node in cgf.items()]) - # pool.close() -# ======= results = pool.starmap(create_test, [(usage_str, node,label,base_isa,max_inst, op_template, randomize, out_dir, xlen, flen) for label,node in cgf.items()]) pool.close() -# >>>>>>> master