1#/usr/bin/python3 2# -*- coding: UTF-8 -*- 3import sys 4import re 5import copy 6import pprint 7 8LINE_COVERRED = "LINE_COVERRED" 9NOT_LINE_COVERRED = "NOT_LINE_COVERRED" 10TOGGLE_COVERRED = "TOGGLE_COVERRED" 11NOT_TOGGLE_COVERRED = "NOT_TOGGLE_COVERRED" 12DONTCARE = "DONTCARE" 13 14BEGIN = "BEGIN" 15END = "END" 16CHILDREN = "CHILDREN" 17MODULE = "MODULE" 18INSTANCE = "INSTANCE" 19TYPE = "TYPE" 20ROOT = "ROOT" 21NODE = "NODE" 22SELFCOVERAGE = "SELFCOVERAGE" 23TREECOVERAGE = "TREECOVERAGE" 24LINECOVERAGE = 0 25TOGGLECOVERAGE = 1 26 27def check_one_hot(l): 28 cnt = 0 29 for e in l: 30 if e: 31 cnt += 1 32 return cnt <= 1 33 34def get_lines(input_file): 35 lines = [] 36 with open(input_file) as f: 37 for line in f: 38 lines.append(line) 39 return lines 40 41def get_line_annotation(lines): 42 line_annotations = [] 43 # pattern_1: 040192 if(array_0_MPORT_en & array_0_MPORT_mask) begin 44 # pattern_2: 2218110 end else if (_T_30) begin // @[Conditional.scala 40:58] 45 # pattern_2: 000417 end else begin 46 line_coverred_pattern_1 = re.compile('^\s*(\d+)\s+if') 47 line_coverred_pattern_2 = re.compile('^\s*(\d+)\s+end else') 48 not_line_coverred_pattern_1 = re.compile('^\s*(%0+)\s+if') 49 not_line_coverred_pattern_2 = re.compile('^\s*(%0+)\s+end else') 50 51 toggle_coverred_pattern_1 = re.compile('^\s*(\d+)\s+reg') 52 toggle_coverred_pattern_2 = re.compile('^\s*(\d+)\s+wire') 53 toggle_coverred_pattern_3 = re.compile('^\s*(\d+)\s+input') 54 toggle_coverred_pattern_4 = re.compile('^\s*(\d+)\s+output') 55 56 not_toggle_coverred_pattern_1 = re.compile('^\s*(%0+)\s+reg') 57 not_toggle_coverred_pattern_2 = re.compile('^\s*(%0+)\s+wire') 58 not_toggle_coverred_pattern_3 = re.compile('^\s*(%0+)\s+input') 59 not_toggle_coverred_pattern_4 = re.compile('^\s*(%0+)\s+output') 60 61 line_cnt = 0 62 63 for line in lines: 64 line_coverred_match = line_coverred_pattern_1.search(line) or line_coverred_pattern_2.search(line) 65 not_line_coverred_match = not_line_coverred_pattern_1.search(line) or not_line_coverred_pattern_2.search(line) 66 67 assert not (line_coverred_match and not_line_coverred_match) 68 69 toggle_coverred_match = toggle_coverred_pattern_1.search(line) or toggle_coverred_pattern_2.search(line) or \ 70 toggle_coverred_pattern_3.search(line) or toggle_coverred_pattern_4.search(line) 71 not_toggle_coverred_match = not_toggle_coverred_pattern_1.search(line) or not_toggle_coverred_pattern_2.search(line) or \ 72 not_toggle_coverred_pattern_3.search(line) or not_toggle_coverred_pattern_4.search(line) 73 74 assert not (toggle_coverred_match and not_toggle_coverred_match) 75 76 all_match = (line_coverred_match, not_line_coverred_match, 77 toggle_coverred_match, not_toggle_coverred_match) 78 if not check_one_hot(all_match): 79 print("not_one_hot") 80 print(line_cnt) 81 print(all_match) 82 assert False, "This line matches multiple patterns" 83 if line_coverred_match: 84 line_annotations.append(LINE_COVERRED) 85 elif not_line_coverred_match: 86 line_annotations.append(NOT_LINE_COVERRED) 87 elif toggle_coverred_match: 88 line_annotations.append(TOGGLE_COVERRED) 89 elif not_toggle_coverred_match: 90 line_annotations.append(NOT_TOGGLE_COVERRED) 91 else: 92 line_annotations.append(DONTCARE) 93 line_cnt += 1 94 return line_annotations 95 96# get the line coverage statistics in line range [start, end) 97def get_coverage_statistics(line_annotations, start, end): 98 line_coverred = 0 99 not_line_coverred = 0 100 toggle_coverred = 0 101 not_toggle_coverred = 0 102 for i in range(start, end): 103 if line_annotations[i] == LINE_COVERRED: 104 line_coverred += 1 105 106 if line_annotations[i] == NOT_LINE_COVERRED: 107 not_line_coverred += 1 108 109 if line_annotations[i] == TOGGLE_COVERRED: 110 toggle_coverred += 1 111 112 if line_annotations[i] == NOT_TOGGLE_COVERRED: 113 not_toggle_coverred += 1 114 115 # deal with divide by zero 116 line_coverage = 1.0 117 if line_coverred + not_line_coverred != 0: 118 line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 119 120 toggle_coverage = 1.0 121 if toggle_coverred + not_toggle_coverred != 0: 122 toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 123 return ((line_coverred, not_line_coverred, line_coverage), 124 (toggle_coverred, not_toggle_coverred, toggle_coverage)) 125 126# get modules and all it's submodules 127def get_modules(lines): 128 modules = {} 129 130 module_pattern = re.compile("module (\w+)\(") 131 endmodule_pattern = re.compile("endmodule") 132 submodule_pattern = re.compile("(\w+) (\w+) \( // @\[\w+.scala \d+:\d+\]") 133 134 line_count = 0 135 136 name = "ModuleName" 137 138 for line in lines: 139 module_match = module_pattern.search(line) 140 endmodule_match = endmodule_pattern.search(line) 141 submodule_match = submodule_pattern.search(line) 142 143 assert not (module_match and endmodule_match) 144 145 if module_match: 146 name = module_match.group(1) 147 # print("module_match: module: %s" % name) 148 assert name not in modules 149 # [begin 150 modules[name] = {} 151 modules[name][BEGIN] = line_count 152 # the first time we see a module, we treat as a root node 153 modules[name][TYPE] = ROOT 154 155 if endmodule_match: 156 # print("endmodule_match: module: %s" % name) 157 assert name in modules 158 assert END not in modules[name] 159 # end) 160 modules[name][END] = line_count + 1 161 # reset module name to invalid 162 name = "ModuleName" 163 164 if submodule_match: 165 # submodule must be inside hierarchy 166 assert name != "ModuleName" 167 submodule_type = submodule_match.group(1) 168 submodule_instance = submodule_match.group(2) 169 # print("submodule_match: type: %s instance: %s" % (submodule_type, submodule_instance)) 170 171 # submodules should be defined first 172 # if we can not find it's definition 173 # we consider it a black block module 174 if submodule_type not in modules: 175 print("Module %s is a Blackbox" % submodule_type) 176 else: 177 # mark submodule as a tree node 178 # it's no longer root any more 179 modules[submodule_type][TYPE] = NODE 180 181 if CHILDREN not in modules[name]: 182 modules[name][CHILDREN] = [] 183 submodule = {MODULE: submodule_type, INSTANCE: submodule_instance} 184 modules[name][CHILDREN].append(submodule) 185 186 line_count += 1 187 return modules 188 189# we define two coverage metrics: 190# self coverage: coverage results of this module(excluding submodules) 191# tree coverage: coverage results of this module(including submodules) 192def get_tree_coverage(modules, coverage): 193 def dfs(module): 194 if TREECOVERAGE not in modules[module]: 195 self_coverage = modules[module][SELFCOVERAGE] 196 if CHILDREN not in modules[module]: 197 modules[module][TREECOVERAGE] = self_coverage 198 else: 199 line_coverred = self_coverage[LINECOVERAGE][0] 200 not_line_coverred = self_coverage[LINECOVERAGE][1] 201 toggle_coverred = self_coverage[TOGGLECOVERAGE][0] 202 not_toggle_coverred = self_coverage[TOGGLECOVERAGE][1] 203 # the dfs part 204 for child in modules[module][CHILDREN]: 205 child_coverage = dfs(child[MODULE]) 206 line_coverred += child_coverage[LINECOVERAGE][0] 207 not_line_coverred += child_coverage[LINECOVERAGE][1] 208 toggle_coverred += child_coverage[TOGGLECOVERAGE][0] 209 not_toggle_coverred += child_coverage[TOGGLECOVERAGE][1] 210 # deal with divide by zero 211 line_coverage = 1.0 212 if line_coverred + not_line_coverred != 0: 213 line_coverage = float(line_coverred) / (line_coverred + not_line_coverred) 214 toggle_coverage = 1.0 215 if toggle_coverred + not_toggle_coverred != 0: 216 toggle_coverage = float(toggle_coverred) / (toggle_coverred + not_toggle_coverred) 217 modules[module][TREECOVERAGE] = ((line_coverred, not_line_coverred, line_coverage), 218 (toggle_coverred, not_toggle_coverred, toggle_coverage)) 219 return modules[module][TREECOVERAGE] 220 221 for module in modules: 222 modules[module][SELFCOVERAGE] = coverage[module] 223 224 for module in modules: 225 modules[module][TREECOVERAGE] = dfs(module) 226 return modules 227 228# arg1: tree coverage results 229# arg2: coverage type 230def sort_coverage(coverage, self_or_tree, coverage_type): 231 l = [(module, coverage[module][self_or_tree][coverage_type])for module in coverage] 232 l.sort(key=lambda x:x[1][2]) 233 return l 234 235def print_tree_coverage(tree_coverage): 236 def dfs(module, level): 237 # print current node 238 tree = tree_coverage[module][TREECOVERAGE] 239 self = tree_coverage[module][SELFCOVERAGE] 240 print(" " * level + "- " + module) 241 print(" " * level + " tree_line", end="") 242 print("(%d, %d, %.2f)" % (tree[LINECOVERAGE][0], tree[LINECOVERAGE][1], tree[LINECOVERAGE][2] * 100.0)) 243 print(" " * level + " self_line", end="") 244 print("(%d, %d, %.2f)" % (self[LINECOVERAGE][0], self[LINECOVERAGE][1], self[LINECOVERAGE][2] * 100.0)) 245 246 print(" " * level + " tree_toggle", end="") 247 print("(%d, %d, %.2f)" % (tree[TOGGLECOVERAGE][0], tree[TOGGLECOVERAGE][1], tree[TOGGLECOVERAGE][2] * 100.0)) 248 print(" " * level + " self_toggle", end="") 249 print("(%d, %d, %.2f)" % (self[TOGGLECOVERAGE][0], self[TOGGLECOVERAGE][1], self[TOGGLECOVERAGE][2] * 100.0)) 250 251 # print children nodes 252 if CHILDREN in modules[module]: 253 # the dfs part 254 for child in modules[module][CHILDREN]: 255 dfs(child[MODULE], level + 1) 256 257 for module in tree_coverage: 258 if tree_coverage[module][TYPE] == ROOT: 259 dfs(module, 0) 260 261if __name__ == "__main__": 262 assert len(sys.argv) == 2, "Expect input_file" 263 input_file = sys.argv[1] 264 pp = pprint.PrettyPrinter(indent=4) 265 266 lines = get_lines(input_file) 267 # print("lines:") 268 # pp.pprint(lines) 269 270 annotations = get_line_annotation(lines) 271 # print("annotations:") 272 # pp.pprint(annotations) 273 274 modules = get_modules(lines) 275 # print("modules:") 276 # pp.pprint(modules) 277 278 self_coverage = {module: get_coverage_statistics(annotations, modules[module][BEGIN], modules[module][END]) 279 for module in modules} 280 # print("self_coverage:") 281 # pp.pprint(self_coverage) 282 283 tree_coverage = get_tree_coverage(modules, self_coverage) 284 # print("tree_coverage:") 285 # pp.pprint(tree_coverage) 286 287 print("LineSelfCoverage:") 288 pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, LINECOVERAGE)) 289 print("LineTreeCoverage:") 290 pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, LINECOVERAGE)) 291 292 print("ToggleSelfCoverage:") 293 pp.pprint(sort_coverage(tree_coverage, SELFCOVERAGE, TOGGLECOVERAGE)) 294 print("ToggleTreeCoverage:") 295 pp.pprint(sort_coverage(tree_coverage, TREECOVERAGE, TOGGLECOVERAGE)) 296 297 print("AllCoverage:") 298 print_tree_coverage(tree_coverage) 299