blob: 66aa0c70f0b3bc7f589021ff066ae5b1b58457f9 [file] [log] [blame]
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -07001#!/usr/bin/env python3
2# Copyright lowRISC contributors.
3# Licensed under the Apache License, Version 2.0, see LICENSE for details.
4# SPDX-License-Identifier: Apache-2.0
5"""
6This script provides common DV simulation specific utilities.
7"""
8
9import re
10from collections import OrderedDict
Cindy Chenf4890c42022-07-20 13:52:21 -070011from typing import List, Tuple
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070012
13
14# Capture the summary results as a list of lists.
15# The text coverage report is passed as input to the function, in addition to
16# the tool used. The tool returns a 2D list if the coverage report file was read
17# and the coverage was extracted successfully. It returns a tuple of:
18# List of metrics and values
19# Final coverage total
Srikrishna Iyer9b381412021-08-10 23:11:43 -070020#
21# Raises the appropriate exception if the coverage summary extraction fails.
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070022def get_cov_summary_table(cov_report_txt, tool):
Srikrishna Iyer9b381412021-08-10 23:11:43 -070023 with open(cov_report_txt, 'r') as f:
24 if tool == 'xcelium':
25 return xcelium_cov_summary_table(f)
26 if tool == 'vcs':
27 return vcs_cov_summary_table(f)
28 raise NotImplementedError(f"{tool} is unsupported for cov extraction.")
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070029
30
31# Same desc as above, but specific to Xcelium and takes an opened input stream.
32def xcelium_cov_summary_table(buf):
33 for line in buf:
34 if "name" in line:
35 # Strip the line and remove the unwanted "* Covered" string.
36 metrics = line.strip().replace("* Covered", "").split()
37 # Change first item to 'Score'.
38 metrics[0] = 'Score'
39
Srikrishna Iyer21721e32021-08-11 02:42:03 -070040 # Gather the list of metrics.
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070041 items = OrderedDict()
42 for metric in metrics:
43 items[metric] = {}
44 items[metric]['covered'] = 0
45 items[metric]['total'] = 0
Srikrishna Iyer21721e32021-08-11 02:42:03 -070046
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070047 # Next line is a separator.
48 line = buf.readline()
Srikrishna Iyer21721e32021-08-11 02:42:03 -070049
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070050 # Subsequent lines are coverage items to be aggregated.
51 for line in buf:
52 line = re.sub(r"%\s+\(", "%(", line)
53 values = line.strip().split()
54 for i, value in enumerate(values):
55 value = value.strip()
Srikrishna Iyer9b381412021-08-10 23:11:43 -070056 m = re.search(r"\((\d+)/(\d+).*\)", value)
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070057 if m:
58 items[metrics[i]]['covered'] += int(m.group(1))
59 items[metrics[i]]['total'] += int(m.group(2))
60 items['Score']['covered'] += int(m.group(1))
61 items['Score']['total'] += int(m.group(2))
62 # Capture the percentages and the aggregate.
63 values = []
64 cov_total = None
65 for metric in items.keys():
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010066 if items[metric]['total'] == 0:
67 values.append("-- %")
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070068 else:
69 value = items[metric]['covered'] / items[metric][
70 'total'] * 100
71 value = "{0:.2f} %".format(round(value, 2))
72 values.append(value)
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010073 if metric == 'Score':
74 cov_total = value
Srikrishna Iyer21721e32021-08-11 02:42:03 -070075 return [items.keys(), values], cov_total
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070076
77 # If we reached here, then we were unable to extract the coverage.
Srikrishna Iyer9b381412021-08-10 23:11:43 -070078 raise SyntaxError(f"Coverage data not found in {buf.name}!")
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -070079
80
81# Same desc as above, but specific to VCS and takes an opened input stream.
82def vcs_cov_summary_table(buf):
83 for line in buf:
84 match = re.match("total coverage summary", line, re.IGNORECASE)
85 if match:
86 # Metrics on the next line.
87 line = buf.readline().strip()
88 metrics = line.split()
89 # Values on the next.
90 line = buf.readline().strip()
91 # Pretty up the values - add % sign for ease of post
92 # processing.
93 values = []
94 for val in line.split():
95 val += " %"
96 values.append(val)
97 # first row is coverage total
98 cov_total = values[0]
Srikrishna Iyer9b381412021-08-10 23:11:43 -070099 return [metrics, values], cov_total
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700100
101 # If we reached here, then we were unable to extract the coverage.
Srikrishna Iyer9b381412021-08-10 23:11:43 -0700102 raise SyntaxError(f"Coverage data not found in {buf.name}!")
Cindy Chenf4890c42022-07-20 13:52:21 -0700103
104
105def get_job_runtime(log_text: List, tool: str) -> Tuple[float, str]:
106 """Returns the job runtime (wall clock time) along with its units.
107
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700108 EDA tools indicate how long the job ran in terms of CPU time in the log
109 file. This method invokes the tool specific method which parses the log
110 text and returns the runtime as a floating point value followed by its
111 units as a tuple.
Cindy Chenf4890c42022-07-20 13:52:21 -0700112
113 `log_text` is the job's log file contents as a list of lines.
114 `tool` is the EDA tool used to run the job.
115 Returns the runtime, units as a tuple.
116 Raises NotImplementedError exception if the EDA tool is not supported.
117 """
118 if tool == 'xcelium':
119 return xcelium_job_runtime(log_text)
120 elif tool == 'vcs':
121 return vcs_job_runtime(log_text)
122 else:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700123 raise NotImplementedError(f"{tool} is unsupported for job runtime "
124 "extraction.")
Cindy Chenf4890c42022-07-20 13:52:21 -0700125
126
127def vcs_job_runtime(log_text: List) -> Tuple[float, str]:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700128 """Returns the VCS job runtime (wall clock time) along with its units.
Cindy Chenf4890c42022-07-20 13:52:21 -0700129
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700130 Search pattern example:
131 CPU time: 22.170 seconds to compile + .518 seconds to elab + 1.901 \
132 seconds to link
133 CPU Time: 0.610 seconds; Data structure size: 1.6Mb
134
135 Returns the runtime, units as a tuple.
136 Raises RuntimeError exception if the search pattern is not found.
Cindy Chenf4890c42022-07-20 13:52:21 -0700137 """
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700138 pattern = r"^CPU [tT]ime:\s*(\d+\.?\d*?)\s*(seconds|minutes|hours).*$"
Cindy Chenf4890c42022-07-20 13:52:21 -0700139 for line in reversed(log_text):
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700140 m = re.search(pattern, line)
141 if m:
142 return float(m.group(1)), m.group(2)[0]
143 raise RuntimeError("Job runtime not found in the log.")
Cindy Chenf4890c42022-07-20 13:52:21 -0700144
145
146def xcelium_job_runtime(log_text: List) -> Tuple[float, str]:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700147 """Returns the Xcelium job runtime (wall clock time) along with its units.
Cindy Chenf4890c42022-07-20 13:52:21 -0700148
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700149 Search pattern example:
150 TOOL: xrun(64) 21.09-s006: Exiting on Aug 01, 2022 at 00:21:18 PDT \
151 (total: 00:00:05)
152
153 Returns the runtime, units as a tuple.
154 Raises RuntimeError exception if the search pattern is not found.
Cindy Chenf4890c42022-07-20 13:52:21 -0700155 """
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700156 pattern = (r"^TOOL:\s*xrun.*: Exiting on .*\(total:\s*(\d+):(\d+):(\d+)\)"
157 r"\s*$")
Cindy Chenf4890c42022-07-20 13:52:21 -0700158 for line in reversed(log_text):
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700159 m = re.search(pattern, line)
160 if m:
161 t = int(m.group(1)) * 3600 + int(m.group(2)) * 60 + int(m.group(3))
162 return t, "s"
163 raise RuntimeError("Job runtime not found in the log.")
Cindy Chenf4890c42022-07-20 13:52:21 -0700164
165
166def get_simulated_time(log_text: List, tool: str) -> Tuple[float, str]:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700167 """Returns the simulated time along with its units.
168
169 EDA tools indicate how long the design was simulated for in the log file.
Cindy Chenf4890c42022-07-20 13:52:21 -0700170 This method invokes the tool specific method which parses the log text and
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700171 returns the simulated time as a floating point value followed by its
172 units (typically, pico|nano|micro|milliseconds) as a tuple.
173
Cindy Chenf4890c42022-07-20 13:52:21 -0700174 `log_text` is the job's log file contents as a list of lines.
175 `tool` is the EDA tool used to run the job.
176 Returns the simulated, units as a tuple.
177 Raises NotImplementedError exception if the EDA tool is not supported.
178 """
Cindy Chenf4890c42022-07-20 13:52:21 -0700179 if tool == 'xcelium':
180 return xcelium_simulated_time(log_text)
181 elif tool == 'vcs':
182 return vcs_simulated_time(log_text)
183 else:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700184 raise NotImplementedError(f"{tool} is unsupported for simulated time "
185 "extraction.")
Cindy Chenf4890c42022-07-20 13:52:21 -0700186
187
188def xcelium_simulated_time(log_text: List) -> Tuple[float, str]:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700189 """Returns the Xcelium simulated time along with its units.
Cindy Chenf4890c42022-07-20 13:52:21 -0700190
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700191 Search pattern example:
192 Simulation complete via $finish(2) at time 11724965 PS + 13
193
194 Returns the simulated time, units as a tuple.
195 Raises RuntimeError exception if the search pattern is not found.
Cindy Chenf4890c42022-07-20 13:52:21 -0700196 """
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700197 pattern = r"^Simulation complete .* at time (\d+\.?\d*?)\s*(.?[sS]).*$"
Cindy Chenf4890c42022-07-20 13:52:21 -0700198 for line in reversed(log_text):
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700199 m = re.search(pattern, line)
200 if m:
201 return float(m.group(1)), m.group(2).lower()
202 raise RuntimeError("Simulated time not found in the log.")
Cindy Chenf4890c42022-07-20 13:52:21 -0700203
204
205def vcs_simulated_time(log_text: List) -> Tuple[float, str]:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700206 """Returns the VCS simulated time along with its units.
Cindy Chenf4890c42022-07-20 13:52:21 -0700207
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700208 Search pattern example:
209 V C S S i m u l a t i o n R e p o r t
210 Time: 12241752 ps
211
212 Returns the simulated time, units as a tuple.
213 Raises RuntimeError exception if the search pattern is not found.
Cindy Chenf4890c42022-07-20 13:52:21 -0700214 """
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700215 pattern = r"^Time:\s*(\d+\.?\d*?)\s*(.?[sS])\s*$"
216 next_line = ""
Cindy Chenf4890c42022-07-20 13:52:21 -0700217 for line in reversed(log_text):
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700218 if "V C S S i m u l a t i o n R e p o r t" in line:
219 m = re.search(pattern, next_line)
Cindy Chenf4890c42022-07-20 13:52:21 -0700220 if m:
Srikrishna Iyera8485b52022-08-01 13:29:12 -0700221 return float(m.group(1)), m.group(2).lower()
222 next_line = line
223 raise RuntimeError("Simulated time not found in the log.")