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