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