1d70a2272SAllen#/usr/bin/python3 2d70a2272SAllen# -*- coding: UTF-8 -*- 3*c6d43980SLemover 4*c6d43980SLemover#*************************************************************************************** 5*c6d43980SLemover# Copyright (c) 2020-2021 Institute of Computing Technology, Chinese Academy of Sciences 6*c6d43980SLemover# 7*c6d43980SLemover# XiangShan is licensed under Mulan PSL v2. 8*c6d43980SLemover# You can use this software according to the terms and conditions of the Mulan PSL v2. 9*c6d43980SLemover# You may obtain a copy of Mulan PSL v2 at: 10*c6d43980SLemover# http://license.coscl.org.cn/MulanPSL2 11*c6d43980SLemover# 12*c6d43980SLemover# THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 13*c6d43980SLemover# EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 14*c6d43980SLemover# MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. 15*c6d43980SLemover# 16*c6d43980SLemover# See the Mulan PSL v2 for more details. 17*c6d43980SLemover#*************************************************************************************** 18*c6d43980SLemover 19*c6d43980SLemover 20d70a2272SAllenimport sys 21d70a2272SAllenimport re 22d70a2272SAllenimport copy 2343676441SAllenimport pprint 24d70a2272SAllen 25c8b1e4dbSAllenLINE_COVERRED = "LINE_COVERRED" 26c8b1e4dbSAllenNOT_LINE_COVERRED = "NOT_LINE_COVERRED" 27c8b1e4dbSAllenTOGGLE_COVERRED = "TOGGLE_COVERRED" 28c8b1e4dbSAllenNOT_TOGGLE_COVERRED = "NOT_TOGGLE_COVERRED" 2943676441SAllenDONTCARE = "DONTCARE" 30c8b1e4dbSAllen 3143676441SAllenBEGIN = "BEGIN" 3243676441SAllenEND = "END" 3343676441SAllenCHILDREN = "CHILDREN" 3443676441SAllenMODULE = "MODULE" 3543676441SAllenINSTANCE = "INSTANCE" 3643676441SAllenTYPE = "TYPE" 3743676441SAllenROOT = "ROOT" 3843676441SAllenNODE = "NODE" 3943676441SAllenSELFCOVERAGE = "SELFCOVERAGE" 4043676441SAllenTREECOVERAGE = "TREECOVERAGE" 41c8b1e4dbSAllenLINECOVERAGE = 0 42c8b1e4dbSAllenTOGGLECOVERAGE = 1 43c8b1e4dbSAllen 44c8b1e4dbSAllendef check_one_hot(l): 45c8b1e4dbSAllen cnt = 0 46c8b1e4dbSAllen for e in l: 47c8b1e4dbSAllen if e: 48c8b1e4dbSAllen cnt += 1 49c8b1e4dbSAllen return cnt <= 1 5043676441SAllen 5143676441SAllendef get_lines(input_file): 5243676441SAllen lines = [] 53d70a2272SAllen with open(input_file) as f: 54d70a2272SAllen for line in f: 5543676441SAllen lines.append(line) 5643676441SAllen return lines 57d70a2272SAllen 5843676441SAllendef get_line_annotation(lines): 5943676441SAllen line_annotations = [] 6043676441SAllen # pattern_1: 040192 if(array_0_MPORT_en & array_0_MPORT_mask) begin 6143676441SAllen # pattern_2: 2218110 end else if (_T_30) begin // @[Conditional.scala 40:58] 6243676441SAllen # pattern_2: 000417 end else begin 63c8b1e4dbSAllen line_coverred_pattern_1 = re.compile('^\s*(\d+)\s+if') 64c8b1e4dbSAllen line_coverred_pattern_2 = re.compile('^\s*(\d+)\s+end else') 65c8b1e4dbSAllen not_line_coverred_pattern_1 = re.compile('^\s*(%0+)\s+if') 66c8b1e4dbSAllen not_line_coverred_pattern_2 = re.compile('^\s*(%0+)\s+end else') 67c8b1e4dbSAllen 68c8b1e4dbSAllen toggle_coverred_pattern_1 = re.compile('^\s*(\d+)\s+reg') 69c8b1e4dbSAllen toggle_coverred_pattern_2 = re.compile('^\s*(\d+)\s+wire') 70c8b1e4dbSAllen toggle_coverred_pattern_3 = re.compile('^\s*(\d+)\s+input') 71c8b1e4dbSAllen toggle_coverred_pattern_4 = re.compile('^\s*(\d+)\s+output') 72c8b1e4dbSAllen 73c8b1e4dbSAllen not_toggle_coverred_pattern_1 = re.compile('^\s*(%0+)\s+reg') 74c8b1e4dbSAllen not_toggle_coverred_pattern_2 = re.compile('^\s*(%0+)\s+wire') 75c8b1e4dbSAllen not_toggle_coverred_pattern_3 = re.compile('^\s*(%0+)\s+input') 76c8b1e4dbSAllen not_toggle_coverred_pattern_4 = re.compile('^\s*(%0+)\s+output') 77c8b1e4dbSAllen 78c8b1e4dbSAllen line_cnt = 0 7943676441SAllen 8043676441SAllen for line in lines: 81c8b1e4dbSAllen line_coverred_match = line_coverred_pattern_1.search(line) or line_coverred_pattern_2.search(line) 82c8b1e4dbSAllen not_line_coverred_match = not_line_coverred_pattern_1.search(line) or not_line_coverred_pattern_2.search(line) 83d70a2272SAllen 84c8b1e4dbSAllen assert not (line_coverred_match and not_line_coverred_match) 85d70a2272SAllen 86c8b1e4dbSAllen toggle_coverred_match = toggle_coverred_pattern_1.search(line) or toggle_coverred_pattern_2.search(line) or \ 87c8b1e4dbSAllen toggle_coverred_pattern_3.search(line) or toggle_coverred_pattern_4.search(line) 88c8b1e4dbSAllen not_toggle_coverred_match = not_toggle_coverred_pattern_1.search(line) or not_toggle_coverred_pattern_2.search(line) or \ 89c8b1e4dbSAllen not_toggle_coverred_pattern_3.search(line) or not_toggle_coverred_pattern_4.search(line) 90c8b1e4dbSAllen 91c8b1e4dbSAllen assert not (toggle_coverred_match and not_toggle_coverred_match) 92c8b1e4dbSAllen 93c8b1e4dbSAllen all_match = (line_coverred_match, not_line_coverred_match, 94c8b1e4dbSAllen toggle_coverred_match, not_toggle_coverred_match) 95c8b1e4dbSAllen if not check_one_hot(all_match): 96c8b1e4dbSAllen print("not_one_hot") 97c8b1e4dbSAllen print(line_cnt) 98c8b1e4dbSAllen print(all_match) 99c8b1e4dbSAllen assert False, "This line matches multiple patterns" 100c8b1e4dbSAllen if line_coverred_match: 101c8b1e4dbSAllen line_annotations.append(LINE_COVERRED) 102c8b1e4dbSAllen elif not_line_coverred_match: 103c8b1e4dbSAllen line_annotations.append(NOT_LINE_COVERRED) 104c8b1e4dbSAllen elif toggle_coverred_match: 105c8b1e4dbSAllen line_annotations.append(TOGGLE_COVERRED) 106c8b1e4dbSAllen elif not_toggle_coverred_match: 107c8b1e4dbSAllen line_annotations.append(NOT_TOGGLE_COVERRED) 10843676441SAllen else: 10943676441SAllen line_annotations.append(DONTCARE) 110c8b1e4dbSAllen line_cnt += 1 11143676441SAllen return line_annotations 11243676441SAllen 11343676441SAllen# get the line coverage statistics in line range [start, end) 11443676441SAllendef get_coverage_statistics(line_annotations, start, end): 115c8b1e4dbSAllen line_coverred = 0 116c8b1e4dbSAllen not_line_coverred = 0 117c8b1e4dbSAllen toggle_coverred = 0 118c8b1e4dbSAllen not_toggle_coverred = 0 11943676441SAllen for i in range(start, end): 120c8b1e4dbSAllen if line_annotations[i] == LINE_COVERRED: 121c8b1e4dbSAllen line_coverred += 1 122d70a2272SAllen 123c8b1e4dbSAllen if line_annotations[i] == NOT_LINE_COVERRED: 124c8b1e4dbSAllen not_line_coverred += 1 125c8b1e4dbSAllen 126c8b1e4dbSAllen if line_annotations[i] == TOGGLE_COVERRED: 127c8b1e4dbSAllen toggle_coverred += 1 128c8b1e4dbSAllen 129c8b1e4dbSAllen if line_annotations[i] == NOT_TOGGLE_COVERRED: 130c8b1e4dbSAllen not_toggle_coverred += 1 13143676441SAllen 13243676441SAllen # deal with divide by zero 133c8b1e4dbSAllen line_coverage = 1.0 134c8b1e4dbSAllen if line_coverred + not_line_coverred != 0: 135c8b1e4dbSAllen line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 136c8b1e4dbSAllen 137c8b1e4dbSAllen toggle_coverage = 1.0 138c8b1e4dbSAllen if toggle_coverred + not_toggle_coverred != 0: 139c8b1e4dbSAllen toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 140c8b1e4dbSAllen return ((line_coverred, not_line_coverred, line_coverage), 141c8b1e4dbSAllen (toggle_coverred, not_toggle_coverred, toggle_coverage)) 14243676441SAllen 14343676441SAllen# get modules and all it's submodules 14443676441SAllendef get_modules(lines): 14543676441SAllen modules = {} 14643676441SAllen 14743676441SAllen module_pattern = re.compile("module (\w+)\(") 14843676441SAllen endmodule_pattern = re.compile("endmodule") 14943676441SAllen submodule_pattern = re.compile("(\w+) (\w+) \( // @\[\w+.scala \d+:\d+\]") 15043676441SAllen 15143676441SAllen line_count = 0 15243676441SAllen 15343676441SAllen name = "ModuleName" 15443676441SAllen 15543676441SAllen for line in lines: 15643676441SAllen module_match = module_pattern.search(line) 15743676441SAllen endmodule_match = endmodule_pattern.search(line) 15843676441SAllen submodule_match = submodule_pattern.search(line) 15943676441SAllen 16043676441SAllen assert not (module_match and endmodule_match) 16143676441SAllen 16243676441SAllen if module_match: 16343676441SAllen name = module_match.group(1) 16443676441SAllen # print("module_match: module: %s" % name) 16543676441SAllen assert name not in modules 16643676441SAllen # [begin 16743676441SAllen modules[name] = {} 16843676441SAllen modules[name][BEGIN] = line_count 16943676441SAllen # the first time we see a module, we treat as a root node 17043676441SAllen modules[name][TYPE] = ROOT 17143676441SAllen 17243676441SAllen if endmodule_match: 17343676441SAllen # print("endmodule_match: module: %s" % name) 17443676441SAllen assert name in modules 17543676441SAllen assert END not in modules[name] 17643676441SAllen # end) 17743676441SAllen modules[name][END] = line_count + 1 17843676441SAllen # reset module name to invalid 17943676441SAllen name = "ModuleName" 18043676441SAllen 18143676441SAllen if submodule_match: 18243676441SAllen # submodule must be inside hierarchy 18343676441SAllen assert name != "ModuleName" 18443676441SAllen submodule_type = submodule_match.group(1) 18543676441SAllen submodule_instance = submodule_match.group(2) 18643676441SAllen # print("submodule_match: type: %s instance: %s" % (submodule_type, submodule_instance)) 18743676441SAllen 18843676441SAllen # submodules should be defined first 18943676441SAllen # if we can not find it's definition 19043676441SAllen # we consider it a black block module 19143676441SAllen if submodule_type not in modules: 19243676441SAllen print("Module %s is a Blackbox" % submodule_type) 19343676441SAllen else: 19443676441SAllen # mark submodule as a tree node 19543676441SAllen # it's no longer root any more 19643676441SAllen modules[submodule_type][TYPE] = NODE 19743676441SAllen 19843676441SAllen if CHILDREN not in modules[name]: 19943676441SAllen modules[name][CHILDREN] = [] 20043676441SAllen submodule = {MODULE: submodule_type, INSTANCE: submodule_instance} 20143676441SAllen modules[name][CHILDREN].append(submodule) 20243676441SAllen 20343676441SAllen line_count += 1 20443676441SAllen return modules 20543676441SAllen 20643676441SAllen# we define two coverage metrics: 20743676441SAllen# self coverage: coverage results of this module(excluding submodules) 20843676441SAllen# tree coverage: coverage results of this module(including submodules) 20943676441SAllendef get_tree_coverage(modules, coverage): 21043676441SAllen def dfs(module): 21143676441SAllen if TREECOVERAGE not in modules[module]: 21243676441SAllen self_coverage = modules[module][SELFCOVERAGE] 21343676441SAllen if CHILDREN not in modules[module]: 21443676441SAllen modules[module][TREECOVERAGE] = self_coverage 21543676441SAllen else: 216c8b1e4dbSAllen line_coverred = self_coverage[LINECOVERAGE][0] 217c8b1e4dbSAllen not_line_coverred = self_coverage[LINECOVERAGE][1] 218c8b1e4dbSAllen toggle_coverred = self_coverage[TOGGLECOVERAGE][0] 219c8b1e4dbSAllen not_toggle_coverred = self_coverage[TOGGLECOVERAGE][1] 22043676441SAllen # the dfs part 22143676441SAllen for child in modules[module][CHILDREN]: 22243676441SAllen child_coverage = dfs(child[MODULE]) 223c8b1e4dbSAllen line_coverred += child_coverage[LINECOVERAGE][0] 224c8b1e4dbSAllen not_line_coverred += child_coverage[LINECOVERAGE][1] 225c8b1e4dbSAllen toggle_coverred += child_coverage[TOGGLECOVERAGE][0] 226c8b1e4dbSAllen not_toggle_coverred += child_coverage[TOGGLECOVERAGE][1] 22743676441SAllen # deal with divide by zero 228c8b1e4dbSAllen line_coverage = 1.0 229c8b1e4dbSAllen if line_coverred + not_line_coverred != 0: 230c8b1e4dbSAllen line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 231c8b1e4dbSAllen toggle_coverage = 1.0 232c8b1e4dbSAllen if toggle_coverred + not_toggle_coverred != 0: 233c8b1e4dbSAllen toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 234c8b1e4dbSAllen modules[module][TREECOVERAGE] = ((line_coverred, not_line_coverred, line_coverage), 235c8b1e4dbSAllen (toggle_coverred, not_toggle_coverred, toggle_coverage)) 23643676441SAllen return modules[module][TREECOVERAGE] 23743676441SAllen 23843676441SAllen for module in modules: 23943676441SAllen modules[module][SELFCOVERAGE] = coverage[module] 24043676441SAllen 24143676441SAllen for module in modules: 24243676441SAllen modules[module][TREECOVERAGE] = dfs(module) 24343676441SAllen return modules 24443676441SAllen 24543676441SAllen# arg1: tree coverage results 24643676441SAllen# arg2: coverage type 247c8b1e4dbSAllendef sort_coverage(coverage, self_or_tree, coverage_type): 248c8b1e4dbSAllen l = [(module, coverage[module][self_or_tree][coverage_type])for module in coverage] 24943676441SAllen l.sort(key=lambda x:x[1][2]) 25043676441SAllen return l 25143676441SAllen 25243676441SAllendef print_tree_coverage(tree_coverage): 25343676441SAllen def dfs(module, level): 25443676441SAllen # print current node 25543676441SAllen tree = tree_coverage[module][TREECOVERAGE] 25643676441SAllen self = tree_coverage[module][SELFCOVERAGE] 25743676441SAllen print(" " * level + "- " + module) 258c8b1e4dbSAllen print(" " * level + " tree_line", end="") 259c8b1e4dbSAllen print("(%d, %d, %.2f)" % (tree[LINECOVERAGE][0], tree[LINECOVERAGE][1], tree[LINECOVERAGE][2] * 100.0)) 260c8b1e4dbSAllen print(" " * level + " self_line", end="") 261c8b1e4dbSAllen print("(%d, %d, %.2f)" % (self[LINECOVERAGE][0], self[LINECOVERAGE][1], self[LINECOVERAGE][2] * 100.0)) 262c8b1e4dbSAllen 263c8b1e4dbSAllen print(" " * level + " tree_toggle", end="") 264c8b1e4dbSAllen print("(%d, %d, %.2f)" % (tree[TOGGLECOVERAGE][0], tree[TOGGLECOVERAGE][1], tree[TOGGLECOVERAGE][2] * 100.0)) 265c8b1e4dbSAllen print(" " * level + " self_toggle", end="") 266c8b1e4dbSAllen print("(%d, %d, %.2f)" % (self[TOGGLECOVERAGE][0], self[TOGGLECOVERAGE][1], self[TOGGLECOVERAGE][2] * 100.0)) 26743676441SAllen 26843676441SAllen # print children nodes 26943676441SAllen if CHILDREN in modules[module]: 27043676441SAllen # the dfs part 27143676441SAllen for child in modules[module][CHILDREN]: 27243676441SAllen dfs(child[MODULE], level + 1) 27343676441SAllen 27443676441SAllen for module in tree_coverage: 27543676441SAllen if tree_coverage[module][TYPE] == ROOT: 27643676441SAllen dfs(module, 0) 27743676441SAllen 27843676441SAllenif __name__ == "__main__": 27943676441SAllen assert len(sys.argv) == 2, "Expect input_file" 28043676441SAllen input_file = sys.argv[1] 28143676441SAllen pp = pprint.PrettyPrinter(indent=4) 28243676441SAllen 28343676441SAllen lines = get_lines(input_file) 28443676441SAllen # print("lines:") 28543676441SAllen # pp.pprint(lines) 28643676441SAllen 28743676441SAllen annotations = get_line_annotation(lines) 28843676441SAllen # print("annotations:") 28943676441SAllen # pp.pprint(annotations) 29043676441SAllen 29143676441SAllen modules = get_modules(lines) 29243676441SAllen # print("modules:") 29343676441SAllen # pp.pprint(modules) 29443676441SAllen 29543676441SAllen self_coverage = {module: get_coverage_statistics(annotations, modules[module][BEGIN], modules[module][END]) 29643676441SAllen for module in modules} 29743676441SAllen # print("self_coverage:") 29843676441SAllen # pp.pprint(self_coverage) 29943676441SAllen 30043676441SAllen tree_coverage = get_tree_coverage(modules, self_coverage) 30143676441SAllen # print("tree_coverage:") 30243676441SAllen # pp.pprint(tree_coverage) 30343676441SAllen 304c8b1e4dbSAllen print("LineSelfCoverage:") 305c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, LINECOVERAGE)) 306c8b1e4dbSAllen print("LineTreeCoverage:") 307c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, LINECOVERAGE)) 30843676441SAllen 309c8b1e4dbSAllen print("ToggleSelfCoverage:") 310c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, TOGGLECOVERAGE)) 311c8b1e4dbSAllen print("ToggleTreeCoverage:") 312c8b1e4dbSAllen pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, TOGGLECOVERAGE)) 31343676441SAllen 31443676441SAllen print("AllCoverage:") 31543676441SAllen print_tree_coverage(tree_coverage) 316