blob: b906b79ac60c834221b8c3a90f475c2b378fa1df [file] [log] [blame]
Srikrishna Iyer09a81e92019-12-30 10:47:57 -08001# Copyright lowRISC contributors.
2# Licensed under the Apache License, Version 2.0, see LICENSE for details.
3# SPDX-License-Identifier: Apache-2.0
4r"""
5Class describing simulation configuration object
6"""
7
Rupert Swarbrick6cc20112020-04-24 09:44:35 +01008import os
Rupert Swarbrick299cae52020-05-05 11:11:06 +01009import shutil
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010010import subprocess
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080011import sys
Srikrishna Iyer32aae3f2020-03-19 17:34:00 -070012from collections import OrderedDict
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080013
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010014import logging as log
15from tabulate import tabulate
16
17from Deploy import CompileSim, CovAnalyze, CovMerge, CovReport, RunTest, Deploy
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070018from FlowCfg import FlowCfg
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010019from Modes import BuildModes, Modes, Regressions, RunModes, Tests
20from testplanner import testplan_utils
21from utils import VERBOSE, find_and_substitute_wildcards
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080022
23
Rupert Swarbrick299cae52020-05-05 11:11:06 +010024def pick_dump_format(fmts):
25 '''Choose a supported wave dumping format
26
27 fmts is a list of formats that the chosen tool supports. Return the first
28 that we think is possible (e.g. not fsdb if Verdi is not installed).
29
30 '''
31 assert fmts
32 fmt = fmts[0]
33 if fmt == 'fsdb' and not shutil.which('verdi'):
34 return pick_dump_format(fmts[1:])
35
36 return fmt
37
38
39def resolve_dump_format(tool, dump):
40 '''Decide on the correct dumping format
41
42 This is called after reading the config file. tool is the chosen tool,
43 which will always have been resolved by this point. waves is a boolean
44 which determines whether waves should be dumped at all (from the --waves
45 argument). dump is the dumping format chosen on the command line or None.
46
47 '''
48 assert tool is not None
49
50 SUPPORTED_DUMP_FMTS = {
51 'vcs': ['fsdb', 'vpd'],
52 'xcelium': ['fsdb', 'shm', 'vpd']
53 }
54
55 # Look up which dumping formats the tool supports
56 fmts = SUPPORTED_DUMP_FMTS.get(tool)
57
58 if dump is not None:
59 # If the user has specified their preferred dumping format, use it. As
60 # a sanity check, error out if the chosen tool doesn't support the
61 # format, but only if we know about the tool. If not, we'll just assume
62 # they know what they're doing.
63 if fmts is not None and dump not in fmts:
64 log.error('Chosen tool ({}) does not support wave '
65 'dumping format {!r}.'
66 .format(tool, dump))
67 sys.exit(1)
68
69 return dump
70
71 # If the user hasn't specified a dumping format, but has asked for waves,
72 # we need to decide on a format for them. If fmts is None, we don't know
73 # about this tool. Maybe it's a new simulator, in which case, default to
74 # VPD and hope for the best.
75 if not fmts:
76 return 'vpd'
77
78 return pick_dump_format(fmts)
79
80
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080081class SimCfg(FlowCfg):
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080082 """Simulation configuration object
83
84 A simulation configuration class holds key information required for building a DV
85 regression framework.
86 """
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080087 def __init__(self, flow_cfg_file, proj_root, args):
88 super().__init__(flow_cfg_file, proj_root, args)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080089 # Options set from command line
Weicai Yang680f7e22020-02-26 18:10:24 -080090 self.tool = args.tool
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080091 self.build_opts = []
92 self.build_opts.extend(args.build_opts)
Rupert Swarbrick6236f072020-06-02 10:58:21 +010093 self.en_build_modes = args.build_modes.copy()
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080094 self.run_opts = []
95 self.run_opts.extend(args.run_opts)
96 self.en_run_modes = []
97 self.en_run_modes.extend(args.run_modes)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080098 self.build_unique = args.build_unique
99 self.build_only = args.build_only
100 self.run_only = args.run_only
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800101 self.reseed_ovrd = args.reseed
102 self.reseed_multiplier = args.reseed_multiplier
103 self.waves = args.waves
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800104 self.max_waves = args.max_waves
105 self.cov = args.cov
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800106 self.cov_merge_previous = args.cov_merge_previous
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100107 self.profile = args.profile or '(cfg uses profile without --profile)'
Weicai Yangee57d1a2020-01-14 17:13:04 -0800108 self.xprop_off = args.xprop_off
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800109 self.no_rerun = args.no_rerun
110 self.verbosity = "{" + args.verbosity + "}"
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800111 self.verbose = args.verbose
112 self.dry_run = args.dry_run
Srikrishna Iyer64009052020-01-13 11:27:39 -0800113 self.map_full_testplan = args.map_full_testplan
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800114
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700115 # Disable cov if --build-only is passed.
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100116 if self.build_only:
117 self.cov = False
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700118
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800119 # Set default sim modes for unpacking
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100120 if self.waves is True:
121 self.en_build_modes.append("waves")
122 if self.cov is True:
123 self.en_build_modes.append("cov")
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100124 if args.profile is not None:
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100125 self.en_build_modes.append("profile")
126 if self.xprop_off is not True:
127 self.en_build_modes.append("xprop")
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800128
129 # Options built from cfg_file files
130 self.project = ""
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800131 self.flow = ""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800132 self.flow_makefile = ""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800133 self.build_dir = ""
134 self.run_dir = ""
135 self.sw_build_dir = ""
136 self.pass_patterns = []
137 self.fail_patterns = []
138 self.name = ""
139 self.dut = ""
140 self.tb = ""
141 self.testplan = ""
142 self.fusesoc_core = ""
143 self.ral_spec = ""
144 self.build_modes = []
145 self.run_modes = []
146 self.regressions = []
147
Weicai Yang680f7e22020-02-26 18:10:24 -0800148 # Options from tools - for building and running tests
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800149 self.build_cmd = ""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800150 self.flist_gen_cmd = ""
151 self.flist_gen_opts = []
152 self.flist_file = ""
153 self.run_cmd = ""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800154
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800155 # Generated data structures
156 self.links = {}
157 self.build_list = []
158 self.run_list = []
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800159 self.cov_merge_deploy = None
160 self.cov_report_deploy = None
Srikrishna Iyer32aae3f2020-03-19 17:34:00 -0700161 self.results_summary = OrderedDict()
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800162
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700163 # If is_primary_cfg is set, then each cfg will have its own cov_deploy.
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800164 # Maintain an array of those in cov_deploys.
165 self.cov_deploys = []
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800166
167 # Parse the cfg_file file tree
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800168 self.parse_flow_cfg(flow_cfg_file)
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800169 self._post_parse_flow_cfg()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800170
Rupert Swarbrick299cae52020-05-05 11:11:06 +0100171 # Choose a dump format now. Note that this has to happen after parsing
172 # the configuration format because our choice might depend on the
173 # chosen tool.
174 self.dump_fmt = (resolve_dump_format(self.tool, args.dump)
175 if self.waves else 'none')
176
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800177 # If build_unique is set, then add current timestamp to uniquify it
178 if self.build_unique:
179 self.build_dir += "_" + self.timestamp
180
Srikrishna Iyer35292212020-01-17 16:19:35 -0800181 # Process overrides before substituting the wildcards.
182 self._process_overrides()
183
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800184 # Make substitutions, while ignoring the following wildcards
185 # TODO: Find a way to set these in sim cfg instead
186 ignored_wildcards = [
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800187 "build_mode", "index", "test", "seed", "uvm_test", "uvm_test_seq",
Srikrishna Iyerc612bb02020-05-12 00:33:27 -0700188 "cov_db_dirs", "sw_test", "sw_test_is_prebuilt", "sw_build_device"
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800189 ]
190 self.__dict__ = find_and_substitute_wildcards(self.__dict__,
191 self.__dict__,
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800192 ignored_wildcards,
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700193 self.is_primary_cfg)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800194
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800195 # Set the title for simulation results.
196 self.results_title = self.name.upper() + " Simulation Results"
Weicai Yang680f7e22020-02-26 18:10:24 -0800197
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700198 # Stuff below only pertains to individual cfg (not primary cfg)
Cindy Chenf9e7ffe2020-03-26 00:07:36 -0700199 # or individual selected cfgs (if select_cfgs is configured via command line)
200 # TODO: find a better way to support select_cfgs
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700201 if not self.is_primary_cfg and (not self.select_cfgs or
202 self.name in self.select_cfgs):
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100203 # If self.tool is None at this point, there was no --tool argument on
204 # the command line, and there is no default tool set in the config
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700205 # file. That's ok if this is a primary config (where the
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100206 # sub-configurations can choose tools themselves), but not otherwise.
207 if self.tool is None:
208 log.error('Config file does not specify a default tool, '
209 'and there was no --tool argument on the command line.')
210 sys.exit(1)
211
Cindy Chenf9e7ffe2020-03-26 00:07:36 -0700212 # Print info:
Srikrishna Iyerfbaa01a2020-03-19 15:32:23 -0700213 log.info("[scratch_dir]: [%s]: [%s]", self.name, self.scratch_path)
214
215 # Set directories with links for ease of debug / triage.
216 self.links = {
217 "D": self.scratch_path + "/" + "dispatched",
218 "P": self.scratch_path + "/" + "passed",
219 "F": self.scratch_path + "/" + "failed",
220 "K": self.scratch_path + "/" + "killed"
221 }
222
223 # Use the default build mode for tests that do not specify it
224 if not hasattr(self, "build_mode"):
Rupert Swarbrick39362462020-06-04 15:26:17 +0100225 self.build_mode = 'default'
Srikrishna Iyerfbaa01a2020-03-19 15:32:23 -0700226
227 self._process_exports()
228
229 # Create objects from raw dicts - build_modes, sim_modes, run_modes,
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700230 # tests and regressions, only if not a primary cfg obj
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800231 self._create_objects()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800232
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800233 # Post init checks
234 self.__post_init__()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800235
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800236 def __post_init__(self):
237 # Run some post init checks
238 super().__post_init__()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800239
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700240 def kill(self):
241 '''kill running processes and jobs gracefully
242 '''
243 super().kill()
244 for item in self.cov_deploys:
245 item.kill()
246
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800247 # Purge the output directories. This operates on self.
248 def _purge(self):
Rupert Swarbrick8e577192020-03-24 14:36:30 +0000249 if self.scratch_path:
Srikrishna Iyer64009052020-01-13 11:27:39 -0800250 try:
251 log.info("Purging scratch path %s", self.scratch_path)
252 os.system("/bin/rm -rf " + self.scratch_path)
253 except IOError:
254 log.error('Failed to purge scratch directory %s',
255 self.scratch_path)
256
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800257 def _create_objects(self):
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800258 # Create build and run modes objects
Rupert Swarbrick6236f072020-06-02 10:58:21 +0100259 self.build_modes = Modes.create_modes(BuildModes, self.build_modes)
260 self.run_modes = Modes.create_modes(RunModes, self.run_modes)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800261
262 # Walk through build modes enabled on the CLI and append the opts
263 for en_build_mode in self.en_build_modes:
Rupert Swarbrick6236f072020-06-02 10:58:21 +0100264 build_mode_obj = Modes.find_mode(en_build_mode, self.build_modes)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800265 if build_mode_obj is not None:
266 self.build_opts.extend(build_mode_obj.build_opts)
267 self.run_opts.extend(build_mode_obj.run_opts)
268 else:
269 log.error(
270 "Mode \"%s\" enabled on the the command line is not defined",
271 en_build_mode)
272 sys.exit(1)
273
274 # Walk through run modes enabled on the CLI and append the opts
275 for en_run_mode in self.en_run_modes:
Rupert Swarbrick6236f072020-06-02 10:58:21 +0100276 run_mode_obj = Modes.find_mode(en_run_mode, self.run_modes)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800277 if run_mode_obj is not None:
278 self.run_opts.extend(run_mode_obj.run_opts)
279 else:
280 log.error(
281 "Mode \"%s\" enabled on the the command line is not defined",
282 en_run_mode)
283 sys.exit(1)
284
285 # Create tests from given list of items
Rupert Swarbrick39362462020-06-04 15:26:17 +0100286 self.tests = Tests.create_tests(self.tests, self)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800287
Srikrishna Iyer113d7332020-01-21 14:06:47 -0800288 # Regressions
289 # Parse testplan if provided.
290 if self.testplan != "":
291 self.testplan = testplan_utils.parse_testplan(self.testplan)
292 # Extract tests in each milestone and add them as regression target.
293 self.regressions.extend(self.testplan.get_milestone_regressions())
294
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800295 # Create regressions
Rupert Swarbrick39362462020-06-04 15:26:17 +0100296 self.regressions = Regressions.create_regressions(self.regressions,
297 self, self.tests)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800298
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800299 def _print_list(self):
Srikrishna Iyer64009052020-01-13 11:27:39 -0800300 for list_item in self.list_items:
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800301 log.info("---- List of %s in %s ----", list_item, self.name)
Srikrishna Iyer64009052020-01-13 11:27:39 -0800302 if hasattr(self, list_item):
303 items = getattr(self, list_item)
304 for item in items:
305 log.info(item)
306 else:
307 log.error("Item %s does not exist!", list_item)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800308
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800309 def _create_build_and_run_list(self):
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800310 # Walk through the list of items to run and create the build and run
311 # objects.
312 # Allow multiple regressions to run as long as the do not enable
313 # sim_modes or run_modes
314 def get_overlapping_tests(tests, run_list_names):
315 overlapping_tests = []
316 for test in tests:
317 if test.name in run_list_names:
318 overlapping_tests.append(test)
319 return overlapping_tests
320
321 def prune_items(items, marked_items):
322 pruned_items = []
323 for item in items:
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100324 if item not in marked_items:
325 pruned_items.append(item)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800326 return pruned_items
327
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800328 # Check if there are items to run
329 if self.items == []:
330 log.error(
331 "No items provided for running this simulation / regression")
332 sys.exit(1)
333
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800334 items_list = self.items
335 run_list_names = []
336 marked_items = []
337 # Process regressions first
338 for regression in self.regressions:
339 if regression.name in items_list:
340 overlapping_tests = get_overlapping_tests(
341 regression.tests, run_list_names)
342 if overlapping_tests != []:
Srikrishna Iyerc3811a02020-03-11 14:22:05 -0700343 log.error(
344 "Regression \"%s\" added for run contains tests that overlap with "
345 "other regressions added. This can result in conflicting "
346 "build / run_opts to be set causing unexpected results.",
347 regression.name)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800348 sys.exit(1)
349
350 self.run_list.extend(regression.tests)
351 # Merge regression's build and run opts with its tests and their
352 # build_modes
353 regression.merge_regression_opts()
354 run_list_names.extend(regression.test_names)
355 marked_items.append(regression.name)
356 items_list = prune_items(items_list, marked_items)
357
358 # Process individual tests
359 for test in self.tests:
360 if test.name in items_list:
361 overlapping_tests = get_overlapping_tests([test],
362 run_list_names)
363 if overlapping_tests == []:
364 self.run_list.append(test)
365 run_list_names.append(test.name)
366 marked_items.append(test.name)
367 items_list = prune_items(items_list, marked_items)
368
369 # Merge the global build and run opts
370 Tests.merge_global_opts(self.run_list, self.build_opts, self.run_opts)
371
Srikrishna Iyerc3811a02020-03-11 14:22:05 -0700372 # Check if all items have been processed
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800373 if items_list != []:
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800374 log.error(
Srikrishna Iyerc3811a02020-03-11 14:22:05 -0700375 "The items %s added for run were not found in \n%s!\n "
376 "Use the --list switch to see a list of available "
377 "tests / regressions.", items_list, self.flow_cfg_file)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800378
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800379 # Process reseed override and create the build_list
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800380 build_list_names = []
381 for test in self.run_list:
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800382 # Override reseed if available.
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100383 if self.reseed_ovrd is not None:
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800384 test.reseed = self.reseed_ovrd
385
386 # Apply reseed multiplier if set on the command line.
387 test.reseed *= self.reseed_multiplier
388
389 # Create the unique set of builds needed.
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800390 if test.build_mode.name not in build_list_names:
391 self.build_list.append(test.build_mode)
392 build_list_names.append(test.build_mode.name)
393
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800394 def _create_dirs(self):
395 '''Create initial set of directories
396 '''
397 # Invoking system calls has a performance penalty.
398 # Construct a single command line chained with '&&' to invoke
399 # the system call only once, rather than multiple times.
400 create_link_dirs_cmd = ""
401 for link in self.links.keys():
402 create_link_dirs_cmd += "/bin/rm -rf " + self.links[link] + " && "
403 create_link_dirs_cmd += "mkdir -p " + self.links[link] + " && "
404 create_link_dirs_cmd += " true"
405
406 try:
407 os.system(create_link_dirs_cmd)
408 except IOError:
409 log.error("Error running when running the cmd \"%s\"",
410 create_link_dirs_cmd)
411 sys.exit(1)
412
Rupert Swarbrick7b207b72020-06-11 09:43:46 +0100413 def _expand_run_list(self, build_map):
414 '''Generate a list of tests to be run
415
416 For each test in tests, we add it test.reseed times. The ordering is
417 interleaved so that we run through all of the tests as soon as
418 possible. If there are multiple tests and they have different reseed
419 values, they are "fully interleaved" at the start (so if there are
420 tests A, B with reseed values of 5 and 2, respectively, then the list
421 will be ABABAAA).
422
423 build_map is a dictionary from build name to a CompileSim object. Each
424 test is added to the CompileSim item that it depends on (signifying
425 that the test should be built once the build on which it depends is
426 done).
427
428 cfg is a SimCfg object, passed to the RunTest constructor.
429
430 '''
431 tagged = []
432 for test in self.run_list:
433 for idx in range(test.reseed):
434 tagged.append((idx,
435 test,
436 RunTest(idx, test, self)))
437
438 # Stably sort the tagged list by the 1st coordinate
439 tagged.sort(key=lambda x: x[0])
440
441 # Now iterate over it again, adding tests to build_map (in the
442 # interleaved order) and collecting up the RunTest objects.
443 runs = []
444 for _, test, run in tagged:
445 build_map[test.build_mode].sub.append(run)
446 runs.append(run)
447
448 return runs
449
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800450 def _create_deploy_objects(self):
451 '''Create deploy objects from the build and run lists.
452 '''
453
454 # Create the build and run list first
455 self._create_build_and_run_list()
456
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800457 builds = []
458 build_map = {}
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800459 for build in self.build_list:
460 item = CompileSim(build, self)
461 builds.append(item)
462 build_map[build] = item
463
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800464 self.builds = builds
Rupert Swarbrick7b207b72020-06-11 09:43:46 +0100465 self.runs = ([]
466 if self.build_only
467 else self._expand_run_list(build_map))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800468 if self.run_only is True:
Rupert Swarbrick7b207b72020-06-11 09:43:46 +0100469 self.deploy = self.runs
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800470 else:
471 self.deploy = builds
472
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800473 # Create cov_merge and cov_report objects
474 if self.cov:
475 self.cov_merge_deploy = CovMerge(self)
476 self.cov_report_deploy = CovReport(self)
477 # Generate reports only if merge was successful; add it as a dependency
478 # of merge.
479 self.cov_merge_deploy.sub.append(self.cov_report_deploy)
480
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800481 # Create initial set of directories before kicking off the regression.
482 self._create_dirs()
483
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800484 def create_deploy_objects(self):
485 '''Public facing API for _create_deploy_objects().
486 '''
487 super().create_deploy_objects()
488
489 # Also, create cov_deploys
490 if self.cov:
491 for item in self.cfgs:
Weicai Yang680f7e22020-02-26 18:10:24 -0800492 if item.cov:
493 self.cov_deploys.append(item.cov_merge_deploy)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800494
495 # deploy additional commands as needed. We do this separated for coverage
496 # since that needs to happen at the end.
497 def deploy_objects(self):
498 '''This is a public facing API, so we use "self.cfgs" instead of self.
499 '''
500 # Invoke the base class method to run the regression.
501 super().deploy_objects()
502
503 # If coverage is enabled, then deploy the coverage tasks.
504 if self.cov:
505 Deploy.deploy(self.cov_deploys)
506
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800507 def _cov_analyze(self):
508 '''Use the last regression coverage data to open up the GUI tool to
509 analyze the coverage.
510 '''
511 cov_analyze_deploy = CovAnalyze(self)
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700512 self.deploy = [cov_analyze_deploy]
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800513
514 def cov_analyze(self):
515 '''Public facing API for analyzing coverage.
516 '''
517 for item in self.cfgs:
518 item._cov_analyze()
519
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800520 def _gen_results(self):
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800521 '''
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800522 The function is called after the regression has completed. It collates the
523 status of all run targets and generates a dict. It parses the testplan and
524 maps the generated result to the testplan entries to generate a final table
525 (list). It also prints the full list of failures for debug / triage. If cov
526 is enabled, then the summary coverage report is also generated. The final
527 result is in markdown format.
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800528 '''
529
530 # TODO: add support for html
531 def retrieve_result(name, results):
532 for item in results:
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100533 if name == item["name"]:
534 return item
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800535 return None
536
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800537 def gen_results_sub(items, results, fail_msgs):
Srikrishna Iyer64009052020-01-13 11:27:39 -0800538 '''
539 Generate the results table from the test runs (builds are ignored).
540 The table has 3 columns - name, passing and total as a list of dicts.
541 This is populated for all tests. The number of passing and total is
542 in reference to the number of iterations or reseeds for that test.
543 This list of dicts is directly consumed by the Testplan::results_table
544 method for testplan mapping / annotation.
545 '''
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800546 for item in items:
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800547 if item.status == "F":
548 fail_msgs += item.fail_msg
549
550 # Generate results table for runs.
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800551 if item.target == "run":
552 result = retrieve_result(item.name, results)
553 if result is None:
554 result = {"name": item.name, "passing": 0, "total": 0}
555 results.append(result)
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100556 if item.status == "P":
557 result["passing"] += 1
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800558 result["total"] += 1
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800559 (results, fail_msgs) = gen_results_sub(item.sub, results,
560 fail_msgs)
561 return (results, fail_msgs)
562
563 regr_results = []
564 fail_msgs = ""
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800565 deployed_items = self.deploy
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100566 if self.cov:
567 deployed_items.append(self.cov_merge_deploy)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800568 (regr_results, fail_msgs) = gen_results_sub(deployed_items,
569 regr_results, fail_msgs)
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800570
571 # Add title if there are indeed failures
572 if fail_msgs != "":
573 fail_msgs = "\n## List of Failures\n" + fail_msgs
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800574 self.errors_seen = True
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800575
576 # Generate results table for runs.
Srikrishna Iyerf578e7c2020-01-29 13:11:58 -0800577 results_str = "## " + self.results_title + "\n"
578 results_str += "### " + self.timestamp_long + "\n"
Michael Schaffnercb61dc42020-07-10 16:36:39 -0700579 if self.revision_string:
580 results_str += "### " + self.revision_string + "\n"
Srikrishna Iyerf578e7c2020-01-29 13:11:58 -0800581
582 # Add path to testplan.
Weicai Yang2b1f2192020-04-08 17:26:31 -0700583 if hasattr(self, "testplan_doc_path"):
Rupert Swarbrick39362462020-06-04 15:26:17 +0100584 testplan = "https://" + self.doc_server + '/' + self.testplan_doc_path
Weicai Yang2b1f2192020-04-08 17:26:31 -0700585 else:
586 testplan = "https://" + self.doc_server + '/' + self.rel_path
587 testplan = testplan.replace("/dv", "/doc/dv_plan/#testplan")
588
Weicai Yang680f7e22020-02-26 18:10:24 -0800589 results_str += "### [Testplan](" + testplan + ")\n"
590 results_str += "### Simulator: " + self.tool.upper() + "\n\n"
Srikrishna Iyerf578e7c2020-01-29 13:11:58 -0800591
Srikrishna Iyer8ce80d02020-02-05 10:53:19 -0800592 if regr_results == []:
593 results_str += "No results to display.\n"
594
595 else:
596 # TODO: check if testplan is not null?
597 # Map regr results to the testplan entries.
598 results_str += self.testplan.results_table(
599 regr_results=regr_results,
600 map_full_testplan=self.map_full_testplan)
601 results_str += "\n"
Weicai Yang680f7e22020-02-26 18:10:24 -0800602 self.results_summary = self.testplan.results_summary
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800603
Srikrishna Iyerc3811a02020-03-11 14:22:05 -0700604 # Append coverage results of coverage was enabled.
Srikrishna Iyerc4e32032020-03-20 02:43:07 -0700605 if self.cov:
606 if self.cov_report_deploy.status == "P":
607 results_str += "\n## Coverage Results\n"
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700608 # Link the dashboard page using "cov_report_page" value.
Weicai Yang025d35d2020-04-21 11:15:32 -0700609 if hasattr(self, "cov_report_page"):
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700610 results_str += "\n### [Coverage Dashboard]"
Srikrishna Iyer91e1b762020-07-30 10:00:04 -0700611 if self.args.publish:
612 cov_report_page_path = "cov_report"
613 else:
614 cov_report_page_path = self.cov_report_dir
615 cov_report_page_path += "/" + self.cov_report_page
616 results_str += "({})\n\n".format(cov_report_page_path)
Srikrishna Iyerc4e32032020-03-20 02:43:07 -0700617 results_str += self.cov_report_deploy.cov_results
618 self.results_summary[
619 "Coverage"] = self.cov_report_deploy.cov_total
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700620 else:
621 self.results_summary["Coverage"] = "--"
Weicai Yang680f7e22020-02-26 18:10:24 -0800622
Srikrishna Iyerc3811a02020-03-11 14:22:05 -0700623 # append link of detail result to block name
624 self.results_summary["Name"] = self._get_results_page_link(
625 self.results_summary["Name"])
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800626
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800627 # Append failures for triage
628 self.results_md = results_str + fail_msgs
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800629 results_str += fail_msgs
Srikrishna Iyer64009052020-01-13 11:27:39 -0800630
631 # Write results to the scratch area
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800632 results_file = self.scratch_path + "/results_" + self.timestamp + ".md"
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800633 f = open(results_file, 'w')
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800634 f.write(self.results_md)
Srikrishna Iyer64009052020-01-13 11:27:39 -0800635 f.close()
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800636
637 # Return only the tables
Srikrishna Iyerfbaa01a2020-03-19 15:32:23 -0700638 log.info("[results page]: [%s] [%s]", self.name, results_file)
Srikrishna Iyer64009052020-01-13 11:27:39 -0800639 return results_str
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800640
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800641 def gen_results_summary(self):
642
Weicai Yang680f7e22020-02-26 18:10:24 -0800643 # sim summary result has 5 columns from each SimCfg.results_summary
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800644 header = ["Name", "Passing", "Total", "Pass Rate"]
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100645 if self.cov:
646 header.append('Coverage')
Weicai Yang680f7e22020-02-26 18:10:24 -0800647 table = [header]
648 colalign = ("center", ) * len(header)
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800649 for item in self.cfgs:
Weicai Yang680f7e22020-02-26 18:10:24 -0800650 row = []
651 for title in item.results_summary:
652 row.append(item.results_summary[title])
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100653 if row == []:
654 continue
Srikrishna Iyer32aae3f2020-03-19 17:34:00 -0700655 table.append(row)
Srikrishna Iyer86f6bce2020-02-27 19:02:04 -0800656 self.results_summary_md = "## " + self.results_title + " (Summary)\n"
Weicai Yang680f7e22020-02-26 18:10:24 -0800657 self.results_summary_md += "### " + self.timestamp_long + "\n"
Michael Schaffnercb61dc42020-07-10 16:36:39 -0700658 if self.revision_string:
659 self.results_summary_md += "### " + self.revision_string + "\n"
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800660 self.results_summary_md += tabulate(table,
661 headers="firstrow",
662 tablefmt="pipe",
663 colalign=colalign)
664 print(self.results_summary_md)
665 return self.results_summary_md
666
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800667 def _publish_results(self):
668 '''Publish coverage results to the opentitan web server.'''
669 super()._publish_results()
670
671 if self.cov:
672 results_server_dir_url = self.results_server_dir.replace(
673 self.results_server_prefix, self.results_server_url_prefix)
674
675 log.info("Publishing coverage results to %s",
676 results_server_dir_url)
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100677 cmd = (self.results_server_cmd + " -m cp -R " +
Srikrishna Iyer91e1b762020-07-30 10:00:04 -0700678 self.cov_report_dir + " " + self.results_server_dir)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800679 try:
680 cmd_output = subprocess.run(args=cmd,
681 shell=True,
682 stdout=subprocess.PIPE,
683 stderr=subprocess.STDOUT)
684 log.log(VERBOSE, cmd_output.stdout.decode("utf-8"))
685 except Exception as e:
686 log.error("%s: Failed to publish results:\n\"%s\"", e,
687 str(cmd))