xref: /XiangShan/scripts/coverage/statistics.py (revision f320e0f01bd645f0a3045a8a740e60dd770734a9)
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