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