1d70a2272SAllen#/usr/bin/python3 2d70a2272SAllen# -*- coding: UTF-8 -*- 3d70a2272SAllenimport sys 4d70a2272SAllenimport re 5d70a2272SAllenimport copy 643676441SAllenimport pprint 7d70a2272SAllen 8*c8b1e4dbSAllenLINE_COVERRED = "LINE_COVERRED" 9*c8b1e4dbSAllenNOT_LINE_COVERRED = "NOT_LINE_COVERRED" 10*c8b1e4dbSAllenTOGGLE_COVERRED = "TOGGLE_COVERRED" 11*c8b1e4dbSAllenNOT_TOGGLE_COVERRED = "NOT_TOGGLE_COVERRED" 1243676441SAllenDONTCARE = "DONTCARE" 13*c8b1e4dbSAllen 1443676441SAllenBEGIN = "BEGIN" 1543676441SAllenEND = "END" 1643676441SAllenCHILDREN = "CHILDREN" 1743676441SAllenMODULE = "MODULE" 1843676441SAllenINSTANCE = "INSTANCE" 1943676441SAllenTYPE = "TYPE" 2043676441SAllenROOT = "ROOT" 2143676441SAllenNODE = "NODE" 2243676441SAllenSELFCOVERAGE = "SELFCOVERAGE" 2343676441SAllenTREECOVERAGE = "TREECOVERAGE" 24*c8b1e4dbSAllenLINECOVERAGE = 0 25*c8b1e4dbSAllenTOGGLECOVERAGE = 1 26*c8b1e4dbSAllen 27*c8b1e4dbSAllendef check_one_hot(l): 28*c8b1e4dbSAllen cnt = 0 29*c8b1e4dbSAllen for e in l: 30*c8b1e4dbSAllen if e: 31*c8b1e4dbSAllen cnt += 1 32*c8b1e4dbSAllen return cnt <= 1 3343676441SAllen 3443676441SAllendef get_lines(input_file): 3543676441SAllen lines = [] 36d70a2272SAllen with open(input_file) as f: 37d70a2272SAllen for line in f: 3843676441SAllen lines.append(line) 3943676441SAllen return lines 40d70a2272SAllen 4143676441SAllendef get_line_annotation(lines): 4243676441SAllen line_annotations = [] 4343676441SAllen # pattern_1: 040192 if(array_0_MPORT_en & array_0_MPORT_mask) begin 4443676441SAllen # pattern_2: 2218110 end else if (_T_30) begin // @[Conditional.scala 40:58] 4543676441SAllen # pattern_2: 000417 end else begin 46*c8b1e4dbSAllen line_coverred_pattern_1 = re.compile('^\s*(\d+)\s+if') 47*c8b1e4dbSAllen line_coverred_pattern_2 = re.compile('^\s*(\d+)\s+end else') 48*c8b1e4dbSAllen not_line_coverred_pattern_1 = re.compile('^\s*(%0+)\s+if') 49*c8b1e4dbSAllen not_line_coverred_pattern_2 = re.compile('^\s*(%0+)\s+end else') 50*c8b1e4dbSAllen 51*c8b1e4dbSAllen toggle_coverred_pattern_1 = re.compile('^\s*(\d+)\s+reg') 52*c8b1e4dbSAllen toggle_coverred_pattern_2 = re.compile('^\s*(\d+)\s+wire') 53*c8b1e4dbSAllen toggle_coverred_pattern_3 = re.compile('^\s*(\d+)\s+input') 54*c8b1e4dbSAllen toggle_coverred_pattern_4 = re.compile('^\s*(\d+)\s+output') 55*c8b1e4dbSAllen 56*c8b1e4dbSAllen not_toggle_coverred_pattern_1 = re.compile('^\s*(%0+)\s+reg') 57*c8b1e4dbSAllen not_toggle_coverred_pattern_2 = re.compile('^\s*(%0+)\s+wire') 58*c8b1e4dbSAllen not_toggle_coverred_pattern_3 = re.compile('^\s*(%0+)\s+input') 59*c8b1e4dbSAllen not_toggle_coverred_pattern_4 = re.compile('^\s*(%0+)\s+output') 60*c8b1e4dbSAllen 61*c8b1e4dbSAllen line_cnt = 0 6243676441SAllen 6343676441SAllen for line in lines: 64*c8b1e4dbSAllen line_coverred_match = line_coverred_pattern_1.search(line) or line_coverred_pattern_2.search(line) 65*c8b1e4dbSAllen not_line_coverred_match = not_line_coverred_pattern_1.search(line) or not_line_coverred_pattern_2.search(line) 66d70a2272SAllen 67*c8b1e4dbSAllen assert not (line_coverred_match and not_line_coverred_match) 68d70a2272SAllen 69*c8b1e4dbSAllen toggle_coverred_match = toggle_coverred_pattern_1.search(line) or toggle_coverred_pattern_2.search(line) or \ 70*c8b1e4dbSAllen toggle_coverred_pattern_3.search(line) or toggle_coverred_pattern_4.search(line) 71*c8b1e4dbSAllen not_toggle_coverred_match = not_toggle_coverred_pattern_1.search(line) or not_toggle_coverred_pattern_2.search(line) or \ 72*c8b1e4dbSAllen not_toggle_coverred_pattern_3.search(line) or not_toggle_coverred_pattern_4.search(line) 73*c8b1e4dbSAllen 74*c8b1e4dbSAllen assert not (toggle_coverred_match and not_toggle_coverred_match) 75*c8b1e4dbSAllen 76*c8b1e4dbSAllen all_match = (line_coverred_match, not_line_coverred_match, 77*c8b1e4dbSAllen toggle_coverred_match, not_toggle_coverred_match) 78*c8b1e4dbSAllen if not check_one_hot(all_match): 79*c8b1e4dbSAllen print("not_one_hot") 80*c8b1e4dbSAllen print(line_cnt) 81*c8b1e4dbSAllen print(all_match) 82*c8b1e4dbSAllen assert False, "This line matches multiple patterns" 83*c8b1e4dbSAllen if line_coverred_match: 84*c8b1e4dbSAllen line_annotations.append(LINE_COVERRED) 85*c8b1e4dbSAllen elif not_line_coverred_match: 86*c8b1e4dbSAllen line_annotations.append(NOT_LINE_COVERRED) 87*c8b1e4dbSAllen elif toggle_coverred_match: 88*c8b1e4dbSAllen line_annotations.append(TOGGLE_COVERRED) 89*c8b1e4dbSAllen elif not_toggle_coverred_match: 90*c8b1e4dbSAllen line_annotations.append(NOT_TOGGLE_COVERRED) 9143676441SAllen else: 9243676441SAllen line_annotations.append(DONTCARE) 93*c8b1e4dbSAllen line_cnt += 1 9443676441SAllen return line_annotations 9543676441SAllen 9643676441SAllen# get the line coverage statistics in line range [start, end) 9743676441SAllendef get_coverage_statistics(line_annotations, start, end): 98*c8b1e4dbSAllen line_coverred = 0 99*c8b1e4dbSAllen not_line_coverred = 0 100*c8b1e4dbSAllen toggle_coverred = 0 101*c8b1e4dbSAllen not_toggle_coverred = 0 10243676441SAllen for i in range(start, end): 103*c8b1e4dbSAllen if line_annotations[i] == LINE_COVERRED: 104*c8b1e4dbSAllen line_coverred += 1 105d70a2272SAllen 106*c8b1e4dbSAllen if line_annotations[i] == NOT_LINE_COVERRED: 107*c8b1e4dbSAllen not_line_coverred += 1 108*c8b1e4dbSAllen 109*c8b1e4dbSAllen if line_annotations[i] == TOGGLE_COVERRED: 110*c8b1e4dbSAllen toggle_coverred += 1 111*c8b1e4dbSAllen 112*c8b1e4dbSAllen if line_annotations[i] == NOT_TOGGLE_COVERRED: 113*c8b1e4dbSAllen not_toggle_coverred += 1 11443676441SAllen 11543676441SAllen # deal with divide by zero 116*c8b1e4dbSAllen line_coverage = 1.0 117*c8b1e4dbSAllen if line_coverred + not_line_coverred != 0: 118*c8b1e4dbSAllen line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 119*c8b1e4dbSAllen 120*c8b1e4dbSAllen toggle_coverage = 1.0 121*c8b1e4dbSAllen if toggle_coverred + not_toggle_coverred != 0: 122*c8b1e4dbSAllen toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 123*c8b1e4dbSAllen return ((line_coverred, not_line_coverred, line_coverage), 124*c8b1e4dbSAllen (toggle_coverred, not_toggle_coverred, toggle_coverage)) 12543676441SAllen 12643676441SAllen# get modules and all it's submodules 12743676441SAllendef get_modules(lines): 12843676441SAllen modules = {} 12943676441SAllen 13043676441SAllen module_pattern = re.compile("module (\w+)\(") 13143676441SAllen endmodule_pattern = re.compile("endmodule") 13243676441SAllen submodule_pattern = re.compile("(\w+) (\w+) \( // @\[\w+.scala \d+:\d+\]") 13343676441SAllen 13443676441SAllen line_count = 0 13543676441SAllen 13643676441SAllen name = "ModuleName" 13743676441SAllen 13843676441SAllen for line in lines: 13943676441SAllen module_match = module_pattern.search(line) 14043676441SAllen endmodule_match = endmodule_pattern.search(line) 14143676441SAllen submodule_match = submodule_pattern.search(line) 14243676441SAllen 14343676441SAllen assert not (module_match and endmodule_match) 14443676441SAllen 14543676441SAllen if module_match: 14643676441SAllen name = module_match.group(1) 14743676441SAllen # print("module_match: module: %s" % name) 14843676441SAllen assert name not in modules 14943676441SAllen # [begin 15043676441SAllen modules[name] = {} 15143676441SAllen modules[name][BEGIN] = line_count 15243676441SAllen # the first time we see a module, we treat as a root node 15343676441SAllen modules[name][TYPE] = ROOT 15443676441SAllen 15543676441SAllen if endmodule_match: 15643676441SAllen # print("endmodule_match: module: %s" % name) 15743676441SAllen assert name in modules 15843676441SAllen assert END not in modules[name] 15943676441SAllen # end) 16043676441SAllen modules[name][END] = line_count + 1 16143676441SAllen # reset module name to invalid 16243676441SAllen name = "ModuleName" 16343676441SAllen 16443676441SAllen if submodule_match: 16543676441SAllen # submodule must be inside hierarchy 16643676441SAllen assert name != "ModuleName" 16743676441SAllen submodule_type = submodule_match.group(1) 16843676441SAllen submodule_instance = submodule_match.group(2) 16943676441SAllen # print("submodule_match: type: %s instance: %s" % (submodule_type, submodule_instance)) 17043676441SAllen 17143676441SAllen # submodules should be defined first 17243676441SAllen # if we can not find it's definition 17343676441SAllen # we consider it a black block module 17443676441SAllen if submodule_type not in modules: 17543676441SAllen print("Module %s is a Blackbox" % submodule_type) 17643676441SAllen else: 17743676441SAllen # mark submodule as a tree node 17843676441SAllen # it's no longer root any more 17943676441SAllen modules[submodule_type][TYPE] = NODE 18043676441SAllen 18143676441SAllen if CHILDREN not in modules[name]: 18243676441SAllen modules[name][CHILDREN] = [] 18343676441SAllen submodule = {MODULE: submodule_type, INSTANCE: submodule_instance} 18443676441SAllen modules[name][CHILDREN].append(submodule) 18543676441SAllen 18643676441SAllen line_count += 1 18743676441SAllen return modules 18843676441SAllen 18943676441SAllen# we define two coverage metrics: 19043676441SAllen# self coverage: coverage results of this module(excluding submodules) 19143676441SAllen# tree coverage: coverage results of this module(including submodules) 19243676441SAllendef get_tree_coverage(modules, coverage): 19343676441SAllen def dfs(module): 19443676441SAllen if TREECOVERAGE not in modules[module]: 19543676441SAllen self_coverage = modules[module][SELFCOVERAGE] 19643676441SAllen if CHILDREN not in modules[module]: 19743676441SAllen modules[module][TREECOVERAGE] = self_coverage 19843676441SAllen else: 199*c8b1e4dbSAllen line_coverred = self_coverage[LINECOVERAGE][0] 200*c8b1e4dbSAllen not_line_coverred = self_coverage[LINECOVERAGE][1] 201*c8b1e4dbSAllen toggle_coverred = self_coverage[TOGGLECOVERAGE][0] 202*c8b1e4dbSAllen not_toggle_coverred = self_coverage[TOGGLECOVERAGE][1] 20343676441SAllen # the dfs part 20443676441SAllen for child in modules[module][CHILDREN]: 20543676441SAllen child_coverage = dfs(child[MODULE]) 206*c8b1e4dbSAllen line_coverred += child_coverage[LINECOVERAGE][0] 207*c8b1e4dbSAllen not_line_coverred += child_coverage[LINECOVERAGE][1] 208*c8b1e4dbSAllen toggle_coverred += child_coverage[TOGGLECOVERAGE][0] 209*c8b1e4dbSAllen not_toggle_coverred += child_coverage[TOGGLECOVERAGE][1] 21043676441SAllen # deal with divide by zero 211*c8b1e4dbSAllen line_coverage = 1.0 212*c8b1e4dbSAllen if line_coverred + not_line_coverred != 0: 213*c8b1e4dbSAllen line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 214*c8b1e4dbSAllen toggle_coverage = 1.0 215*c8b1e4dbSAllen if toggle_coverred + not_toggle_coverred != 0: 216*c8b1e4dbSAllen toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 217*c8b1e4dbSAllen modules[module][TREECOVERAGE] = ((line_coverred, not_line_coverred, line_coverage), 218*c8b1e4dbSAllen (toggle_coverred, not_toggle_coverred, toggle_coverage)) 21943676441SAllen return modules[module][TREECOVERAGE] 22043676441SAllen 22143676441SAllen for module in modules: 22243676441SAllen modules[module][SELFCOVERAGE] = coverage[module] 22343676441SAllen 22443676441SAllen for module in modules: 22543676441SAllen modules[module][TREECOVERAGE] = dfs(module) 22643676441SAllen return modules 22743676441SAllen 22843676441SAllen# arg1: tree coverage results 22943676441SAllen# arg2: coverage type 230*c8b1e4dbSAllendef sort_coverage(coverage, self_or_tree, coverage_type): 231*c8b1e4dbSAllen l = [(module, coverage[module][self_or_tree][coverage_type])for module in coverage] 23243676441SAllen l.sort(key=lambda x:x[1][2]) 23343676441SAllen return l 23443676441SAllen 23543676441SAllendef print_tree_coverage(tree_coverage): 23643676441SAllen def dfs(module, level): 23743676441SAllen # print current node 23843676441SAllen tree = tree_coverage[module][TREECOVERAGE] 23943676441SAllen self = tree_coverage[module][SELFCOVERAGE] 24043676441SAllen print(" " * level + "- " + module) 241*c8b1e4dbSAllen print(" " * level + " tree_line", end="") 242*c8b1e4dbSAllen print("(%d, %d, %.2f)" % (tree[LINECOVERAGE][0], tree[LINECOVERAGE][1], tree[LINECOVERAGE][2] * 100.0)) 243*c8b1e4dbSAllen print(" " * level + " self_line", end="") 244*c8b1e4dbSAllen print("(%d, %d, %.2f)" % (self[LINECOVERAGE][0], self[LINECOVERAGE][1], self[LINECOVERAGE][2] * 100.0)) 245*c8b1e4dbSAllen 246*c8b1e4dbSAllen print(" " * level + " tree_toggle", end="") 247*c8b1e4dbSAllen print("(%d, %d, %.2f)" % (tree[TOGGLECOVERAGE][0], tree[TOGGLECOVERAGE][1], tree[TOGGLECOVERAGE][2] * 100.0)) 248*c8b1e4dbSAllen print(" " * level + " self_toggle", end="") 249*c8b1e4dbSAllen print("(%d, %d, %.2f)" % (self[TOGGLECOVERAGE][0], self[TOGGLECOVERAGE][1], self[TOGGLECOVERAGE][2] * 100.0)) 25043676441SAllen 25143676441SAllen # print children nodes 25243676441SAllen if CHILDREN in modules[module]: 25343676441SAllen # the dfs part 25443676441SAllen for child in modules[module][CHILDREN]: 25543676441SAllen dfs(child[MODULE], level + 1) 25643676441SAllen 25743676441SAllen for module in tree_coverage: 25843676441SAllen if tree_coverage[module][TYPE] == ROOT: 25943676441SAllen dfs(module, 0) 26043676441SAllen 26143676441SAllenif __name__ == "__main__": 26243676441SAllen assert len(sys.argv) == 2, "Expect input_file" 26343676441SAllen input_file = sys.argv[1] 26443676441SAllen pp = pprint.PrettyPrinter(indent=4) 26543676441SAllen 26643676441SAllen lines = get_lines(input_file) 26743676441SAllen # print("lines:") 26843676441SAllen # pp.pprint(lines) 26943676441SAllen 27043676441SAllen annotations = get_line_annotation(lines) 27143676441SAllen # print("annotations:") 27243676441SAllen # pp.pprint(annotations) 27343676441SAllen 27443676441SAllen modules = get_modules(lines) 27543676441SAllen # print("modules:") 27643676441SAllen # pp.pprint(modules) 27743676441SAllen 27843676441SAllen self_coverage = {module: get_coverage_statistics(annotations, modules[module][BEGIN], modules[module][END]) 27943676441SAllen for module in modules} 28043676441SAllen # print("self_coverage:") 28143676441SAllen # pp.pprint(self_coverage) 28243676441SAllen 28343676441SAllen tree_coverage = get_tree_coverage(modules, self_coverage) 28443676441SAllen # print("tree_coverage:") 28543676441SAllen # pp.pprint(tree_coverage) 28643676441SAllen 287*c8b1e4dbSAllen print("LineSelfCoverage:") 288*c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, LINECOVERAGE)) 289*c8b1e4dbSAllen print("LineTreeCoverage:") 290*c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, LINECOVERAGE)) 29143676441SAllen 292*c8b1e4dbSAllen print("ToggleSelfCoverage:") 293*c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, TOGGLECOVERAGE)) 294*c8b1e4dbSAllen print("ToggleTreeCoverage:") 295*c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, TOGGLECOVERAGE)) 29643676441SAllen 29743676441SAllen print("AllCoverage:") 29843676441SAllen print_tree_coverage(tree_coverage) 299