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