blob: ae003dbc0bd20ead5fb9f89edc0df92025b8abbd [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
Srikrishna Iyer09a81e92019-12-30 10:47:57 -08004
5import logging as log
6import pprint
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -08007import random
Srikrishna Iyer37b0c992021-03-23 16:27:26 -07008import shlex
Srikrishna Iyer956b3f82021-04-15 15:14:56 -07009from pathlib import Path
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080010
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -080011from LauncherFactory import get_launcher
Rupert Swarbrick6cc20112020-04-24 09:44:35 +010012from sim_utils import get_cov_summary_table
Srikrishna Iyere4662af2020-08-10 13:19:42 -070013from tabulate import tabulate
Srikrishna Iyercaa0d892021-03-01 13:15:52 -080014from utils import (VERBOSE, clean_odirs, find_and_substitute_wildcards,
15 rm_path, subst_wildcards)
Rupert Swarbricka2892a52020-10-12 08:50:08 +010016
17
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080018class Deploy():
19 """
Srikrishna Iyer65783742021-02-10 21:42:12 -080020 Abstraction to create and maintain a runnable job (builds, runs, etc.).
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080021 """
22
Srikrishna Iyer65783742021-02-10 21:42:12 -080023 # Indicate the target for each sub-class.
24 target = None
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080025
Srikrishna Iyer6c531b02020-11-23 18:41:16 -080026 # List of variable names that are to be treated as "list of commands".
Srikrishna Iyer65783742021-02-10 21:42:12 -080027 # This tells '_construct_cmd' that these vars are lists that need to
Srikrishna Iyer6c531b02020-11-23 18:41:16 -080028 # be joined with '&&' instead of a space.
29 cmds_list_vars = []
30
Srikrishna Iyer1d2899f2021-02-11 18:23:15 -080031 # Represents the weight with which a job of this target is scheduled. These
32 # initial weights set for each of the targets below are roughly inversely
33 # proportional to their average runtimes. These are subject to change in
34 # future. Lower the runtime, the higher chance the it gets scheduled. It is
35 # useful to customize this only in case of targets that may coexist at a
36 # time.
37 # TODO: Allow these to be set in the HJson.
38 weight = 1
39
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080040 def __str__(self):
Rupert Swarbrick381770d2020-10-12 08:50:08 +010041 return (pprint.pformat(self.__dict__)
Srikrishna Iyer65783742021-02-10 21:42:12 -080042 if log.getLogger().isEnabledFor(VERBOSE) else self.full_name)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080043
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -080044 def __init__(self, sim_cfg):
Srikrishna Iyer65783742021-02-10 21:42:12 -080045 assert self.target is not None
Srikrishna Iyere4662af2020-08-10 13:19:42 -070046
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -080047 # Cross ref the whole cfg object for ease.
48 self.sim_cfg = sim_cfg
49
Srikrishna Iyer65783742021-02-10 21:42:12 -080050 # A list of jobs on which this job depends.
Rupert Swarbrick381770d2020-10-12 08:50:08 +010051 self.dependencies = []
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080052
Cindy Chen924c6062021-01-27 14:27:50 -080053 # Indicates whether running this job requires all dependencies to pass.
54 # If this flag is set to False, any passing dependency will trigger
55 # this current job to run
56 self.needs_all_dependencies_passing = True
57
Srikrishna Iyer65783742021-02-10 21:42:12 -080058 # Declare attributes that need to be extracted from the HJSon cfg.
59 self._define_attrs()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080060
Srikrishna Iyer65783742021-02-10 21:42:12 -080061 # Set class instance attributes.
62 self._set_attrs()
63
64 # Check if all attributes that are needed are set.
65 self._check_attrs()
66
67 # Do variable substitutions.
68 self._subst_vars()
69
70 # List of vars required to be exported to sub-shell, as a dict.
71 self.exports = self._process_exports()
72
73 # Construct the job's command.
74 self.cmd = self._construct_cmd()
75
Srikrishna Iyerd5623772021-06-18 22:23:07 -070076 # Launcher instance created later using create_launcher() method.
77 self.launcher = None
Srikrishna Iyer65783742021-02-10 21:42:12 -080078
79 def _define_attrs(self):
80 """Defines the attributes this instance needs to have.
81
82 These attributes are extracted from the Mode object / HJson config with
83 which this instance is created. There are two types of attributes -
84 one contributes to the generation of the command directly; the other
85 provides supplementary information pertaining to the job, such as
86 patterns that determine whether it passed or failed. These are
87 represented as dicts, whose values indicate in boolean whether the
88 extraction was successful.
89 """
90 # These attributes are explicitly used to construct the job command.
Srikrishna Iyere4662af2020-08-10 13:19:42 -070091 self.mandatory_cmd_attrs = {}
92
Srikrishna Iyer65783742021-02-10 21:42:12 -080093 # These attributes may indirectly contribute to the construction of the
94 # command (through substitution vars) or other things such as pass /
95 # fail patterns.
Srikrishna Iyere4662af2020-08-10 13:19:42 -070096 self.mandatory_misc_attrs = {
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080097 "name": False,
98 "build_mode": False,
99 "flow_makefile": False,
100 "exports": False,
101 "dry_run": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700102 }
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800103
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800104 # Function to parse a dict and extract the mandatory cmd and misc attrs.
Srikrishna Iyer65783742021-02-10 21:42:12 -0800105 def _extract_attrs(self, ddict):
106 """Extracts the attributes from the supplied dict.
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800107
Srikrishna Iyer65783742021-02-10 21:42:12 -0800108 'ddict' is typically either the Mode object or the entire config
109 object's dict. It is used to retrieve the instance attributes defined
110 in 'mandatory_cmd_attrs' and 'mandatory_misc_attrs'.
111 """
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800112 ddict_keys = ddict.keys()
113 for key in self.mandatory_cmd_attrs.keys():
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100114 if self.mandatory_cmd_attrs[key] is False:
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800115 if key in ddict_keys:
116 setattr(self, key, ddict[key])
117 self.mandatory_cmd_attrs[key] = True
118
119 for key in self.mandatory_misc_attrs.keys():
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100120 if self.mandatory_misc_attrs[key] is False:
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800121 if key in ddict_keys:
122 setattr(self, key, ddict[key])
123 self.mandatory_misc_attrs[key] = True
124
Srikrishna Iyer65783742021-02-10 21:42:12 -0800125 def _set_attrs(self):
126 """Sets additional attributes.
127
128 Invokes '_extract_attrs()' to read in all the necessary instance
129 attributes. Based on those, some additional instance attributes may
130 be derived. Those are set by this method.
131 """
132 self._extract_attrs(self.sim_cfg.__dict__)
133
Srikrishna Iyerd5623772021-06-18 22:23:07 -0700134 # Enable GUI mode.
135 self.gui = self.sim_cfg.gui
136
Srikrishna Iyer65783742021-02-10 21:42:12 -0800137 # Output directory where the artifacts go (used by the launcher).
138 self.odir = getattr(self, self.target + "_dir")
139
140 # Qualified name disambiguates the instance name with other instances
141 # of the same class (example: 'uart_smoke' reseeded multiple times
142 # needs to be disambiguated using the index -> '0.uart_smoke'.
143 self.qual_name = self.name
144
145 # Full name disambiguates across multiple cfg being run (example:
146 # 'aes:default', 'uart:default' builds.
147 self.full_name = self.sim_cfg.name + ":" + self.qual_name
148
Srikrishna Iyer956b3f82021-04-15 15:14:56 -0700149 # Job name is used to group the job by cfg and target. The scratch path
150 # directory name is assumed to be uniquified, in case there are more
151 # than one sim_cfgs with the same name.
152 self.job_name = "{}_{}".format(
153 Path(self.sim_cfg.scratch_path).name, self.target)
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -0800154
Srikrishna Iyer37b0c992021-03-23 16:27:26 -0700155 # Input directories (other than self) this job depends on.
156 self.input_dirs = []
157
158 # Directories touched by this job. These directories are marked
159 # becuase they are used by dependent jobs as input.
160 self.output_dirs = [self.odir]
161
Srikrishna Iyer65783742021-02-10 21:42:12 -0800162 # Pass and fail patterns.
163 self.pass_patterns = []
164 self.fail_patterns = []
165
166 def _check_attrs(self):
167 """Checks if all required class attributes are set.
168
169 Invoked in __init__() after all attributes are extracted and set.
170 """
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800171 for attr in self.mandatory_cmd_attrs.keys():
172 if self.mandatory_cmd_attrs[attr] is False:
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700173 raise AttributeError("Attribute {!r} not found for "
174 "{!r}.".format(attr, self.name))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800175
176 for attr in self.mandatory_misc_attrs.keys():
177 if self.mandatory_misc_attrs[attr] is False:
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700178 raise AttributeError("Attribute {!r} not found for "
179 "{!r}.".format(attr, self.name))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800180
Srikrishna Iyer65783742021-02-10 21:42:12 -0800181 def _subst_vars(self, ignored_subst_vars=[]):
182 """Recursively search and replace substitution variables.
183
184 First pass: search within self dict. We ignore errors since some
185 substitions may be available in the second pass. Second pass: search
186 the entire sim_cfg object."""
187
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700188 self.__dict__ = find_and_substitute_wildcards(self.__dict__,
Srikrishna Iyer65783742021-02-10 21:42:12 -0800189 self.__dict__,
190 ignored_subst_vars, True)
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700191 self.__dict__ = find_and_substitute_wildcards(self.__dict__,
192 self.sim_cfg.__dict__,
Srikrishna Iyer65783742021-02-10 21:42:12 -0800193 ignored_subst_vars,
194 False)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800195
Srikrishna Iyer6333b7c2020-10-17 21:14:25 -0700196 def _process_exports(self):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800197 """Convert 'exports' as a list of dicts in the HJson to a dict.
Srikrishna Iyer6333b7c2020-10-17 21:14:25 -0700198
199 Exports is a list of key-value pairs that are to be exported to the
200 subprocess' environment so that the tools can lookup those options.
201 DVSim limits how the data is presented in the HJson (the value of a
202 HJson member cannot be an object). This method converts a list of dicts
203 into a dict variable, which makes it easy to merge the list of exports
204 with the subprocess' env where the ASIC tool is invoked.
Srikrishna Iyer65783742021-02-10 21:42:12 -0800205 """
Srikrishna Iyer6333b7c2020-10-17 21:14:25 -0700206
Srikrishna Iyer65783742021-02-10 21:42:12 -0800207 return {k: str(v) for item in self.exports for k, v in item.items()}
208
209 def _construct_cmd(self):
210 """Construct the command that will eventually be launched."""
211
Srikrishna Iyer9bff8ea2021-04-02 15:14:04 -0700212 cmd = "make -f {} {}".format(self.flow_makefile, self.target)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800213 if self.dry_run is True:
Srikrishna Iyer9bff8ea2021-04-02 15:14:04 -0700214 cmd += " -n"
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700215 for attr in sorted(self.mandatory_cmd_attrs.keys()):
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800216 value = getattr(self, attr)
217 if type(value) is list:
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800218 # Join attributes that are list of commands with '&&' to chain
219 # them together when executed as a Make target's recipe.
220 separator = " && " if attr in self.cmds_list_vars else " "
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700221 value = separator.join(item.strip() for item in value)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800222 if type(value) is bool:
223 value = int(value)
224 if type(value) is str:
225 value = value.strip()
Srikrishna Iyer9bff8ea2021-04-02 15:14:04 -0700226 cmd += " {}={}".format(attr, shlex.quote(value))
227 return cmd
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800228
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700229 def is_equivalent_job(self, item):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800230 """Checks if job that would be dispatched with 'item' is equivalent to
231 'self'.
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700232
Srikrishna Iyer65783742021-02-10 21:42:12 -0800233 Determines if 'item' and 'self' would behave exactly the same way when
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700234 deployed. If so, then there is no point in keeping both. The caller can
Srikrishna Iyer65783742021-02-10 21:42:12 -0800235 choose to discard 'item' and pick 'self' instead. To do so, we check
236 the final resolved 'cmd' & the exports. The 'name' field will be unique
237 to 'item' and 'self', so we take that out of the comparison.
238 """
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700239 if type(self) != type(item):
240 return False
241
242 # Check if the cmd field is identical.
243 item_cmd = item.cmd.replace(item.name, self.name)
244 if self.cmd != item_cmd:
245 return False
246
247 # Check if exports have identical set of keys.
248 if self.exports.keys() != item.exports.keys():
249 return False
250
251 # Check if exports have identical values.
252 for key, val in self.exports.items():
253 item_val = item.exports[key]
254 if type(item_val) is str:
255 item_val = item_val.replace(item.name, self.name)
256 if val != item_val:
257 return False
258
259 log.log(VERBOSE, "Deploy job \"%s\" is equivalent to \"%s\"",
260 item.name, self.name)
261 return True
262
Srikrishna Iyer65783742021-02-10 21:42:12 -0800263 def pre_launch(self):
264 """Callback to perform additional pre-launch activities.
Srikrishna Iyer6333b7c2020-10-17 21:14:25 -0700265
Srikrishna Iyer65783742021-02-10 21:42:12 -0800266 This is invoked by launcher::_pre_launch().
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800267 """
Srikrishna Iyer65783742021-02-10 21:42:12 -0800268 pass
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800269
Srikrishna Iyer65783742021-02-10 21:42:12 -0800270 def post_finish(self, status):
271 """Callback to perform additional post-finish activities.
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800272
Srikrishna Iyer65783742021-02-10 21:42:12 -0800273 This is invoked by launcher::_post_finish().
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800274 """
Srikrishna Iyer65783742021-02-10 21:42:12 -0800275 pass
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800276
Srikrishna Iyer65783742021-02-10 21:42:12 -0800277 def get_log_path(self):
278 """Returns the log file path."""
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800279
Srikrishna Iyer65783742021-02-10 21:42:12 -0800280 return "{}/{}.log".format(self.odir, self.target)
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700281
Srikrishna Iyerd5623772021-06-18 22:23:07 -0700282 def create_launcher(self):
283 """Creates the launcher instance.
284
285 Note that the launcher instance for ALL jobs in the same job group must
286 be created before the Scheduler starts to dispatch one by one.
287 """
288 # Retain the handle to self for lookup & callbacks.
289 self.launcher = get_launcher(self)
290
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800291
292class CompileSim(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800293 """Abstraction for building the simulation executable."""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800294
Srikrishna Iyer65783742021-02-10 21:42:12 -0800295 target = "build"
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800296 cmds_list_vars = ["pre_build_cmds", "post_build_cmds"]
Srikrishna Iyer1d2899f2021-02-11 18:23:15 -0800297 weight = 5
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800298
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800299 def __init__(self, build_mode, sim_cfg):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800300 self.build_mode_obj = build_mode
Srikrishna Iyer4c4b55d2022-03-29 23:08:47 -0700301 self.seed = sim_cfg.build_seed
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700302 super().__init__(sim_cfg)
303
Srikrishna Iyer65783742021-02-10 21:42:12 -0800304 def _define_attrs(self):
305 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700306 self.mandatory_cmd_attrs.update({
Srikrishna Iyer8ce80d02020-02-05 10:53:19 -0800307 # tool srcs
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800308 "proj_root": False,
Srikrishna Iyer8ce80d02020-02-05 10:53:19 -0800309
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800310 # Flist gen
311 "sv_flist_gen_cmd": False,
312 "sv_flist_gen_dir": False,
313 "sv_flist_gen_opts": False,
314
315 # Build
316 "build_dir": False,
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800317 "pre_build_cmds": False,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800318 "build_cmd": False,
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800319 "build_opts": False,
320 "post_build_cmds": False,
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700321 })
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800322
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700323 self.mandatory_misc_attrs.update({
Weicai Yang31029d22020-03-24 11:14:56 -0700324 "cov_db_dir": False,
325 "build_pass_patterns": False,
326 "build_fail_patterns": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700327 })
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800328
Srikrishna Iyer65783742021-02-10 21:42:12 -0800329 def _set_attrs(self):
330 super()._extract_attrs(self.build_mode_obj.__dict__)
331 super()._set_attrs()
332
Srikrishna Iyerd5623772021-06-18 22:23:07 -0700333 # Dont run the compile job in GUI mode.
334 self.gui = False
335
Srikrishna Iyer65783742021-02-10 21:42:12 -0800336 # 'build_mode' is used as a substitution variable in the HJson.
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800337 self.build_mode = self.name
Srikrishna Iyer956b3f82021-04-15 15:14:56 -0700338 self.job_name += f"_{self.build_mode}"
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700339 if self.sim_cfg.cov:
340 self.output_dirs += [self.cov_db_dir]
Weicai Yang31029d22020-03-24 11:14:56 -0700341 self.pass_patterns = self.build_pass_patterns
342 self.fail_patterns = self.build_fail_patterns
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800343
Srikrishna Iyer65783742021-02-10 21:42:12 -0800344 def pre_launch(self):
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800345 # Delete old coverage database directories before building again. We
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700346 # need to do this because the build directory is not 'renewed'.
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800347 rm_path(self.cov_db_dir)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800348
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800349
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800350class CompileOneShot(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800351 """Abstraction for building the design (used by non-DV flows)."""
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800352
Srikrishna Iyer65783742021-02-10 21:42:12 -0800353 target = "build"
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800354
355 def __init__(self, build_mode, sim_cfg):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800356 self.build_mode_obj = build_mode
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700357 super().__init__(sim_cfg)
358
Srikrishna Iyer65783742021-02-10 21:42:12 -0800359 def _define_attrs(self):
360 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700361 self.mandatory_cmd_attrs.update({
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800362 # tool srcs
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800363 "proj_root": False,
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800364
Michael Schaffner3d160992020-03-31 18:37:53 -0700365 # Flist gen
366 "sv_flist_gen_cmd": False,
367 "sv_flist_gen_dir": False,
368 "sv_flist_gen_opts": False,
369
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800370 # Build
371 "build_dir": False,
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800372 "pre_build_cmds": False,
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800373 "build_cmd": False,
374 "build_opts": False,
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800375 "post_build_cmds": False,
Michael Schaffner3d160992020-03-31 18:37:53 -0700376 "build_log": False,
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800377
378 # Report processing
379 "report_cmd": False,
380 "report_opts": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700381 })
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800382
Srikrishna Iyer9bff8ea2021-04-02 15:14:04 -0700383 self.mandatory_misc_attrs.update({"build_fail_patterns": False})
Srikrishna Iyer5b6f9d62021-04-02 02:12:49 -0700384
Srikrishna Iyer65783742021-02-10 21:42:12 -0800385 def _set_attrs(self):
386 super()._extract_attrs(self.build_mode_obj.__dict__)
387 super()._set_attrs()
388
389 # 'build_mode' is used as a substitution variable in the HJson.
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800390 self.build_mode = self.name
Srikrishna Iyer956b3f82021-04-15 15:14:56 -0700391 self.job_name += f"_{self.build_mode}"
Srikrishna Iyer5b6f9d62021-04-02 02:12:49 -0700392 self.fail_patterns = self.build_fail_patterns
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800393
394
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800395class RunTest(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800396 """Abstraction for running tests. This is one per seed for each test."""
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800397
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800398 # Initial seed values when running tests (if available).
Srikrishna Iyer65783742021-02-10 21:42:12 -0800399 target = "run"
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800400 seeds = []
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700401 fixed_seed = None
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800402 cmds_list_vars = ["pre_run_cmds", "post_run_cmds"]
403
Rupert Swarbrick381770d2020-10-12 08:50:08 +0100404 def __init__(self, index, test, build_job, sim_cfg):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800405 self.test_obj = test
406 self.index = index
407 self.seed = RunTest.get_seed()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700408 super().__init__(sim_cfg)
409
Srikrishna Iyer65783742021-02-10 21:42:12 -0800410 if build_job is not None:
411 self.dependencies.append(build_job)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800412
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -0800413 # We did something wrong if build_mode is not the same as the build_job
414 # arg's name.
415 assert self.build_mode == build_job.name
416
Srikrishna Iyer65783742021-02-10 21:42:12 -0800417 def _define_attrs(self):
418 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700419 self.mandatory_cmd_attrs.update({
Srikrishna Iyere9879ca2020-11-17 01:34:49 -0800420 # tool srcs
Srikrishna Iyer42d032f2020-03-04 23:55:44 -0800421 "proj_root": False,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800422 "uvm_test": False,
423 "uvm_test_seq": False,
Srikrishna Iyerde5efa02020-11-20 23:17:11 -0800424 "sw_images": False,
Srikrishna Iyer7702be52020-03-07 01:00:28 -0800425 "sw_build_device": False,
426 "sw_build_dir": False,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800427 "run_dir": False,
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800428 "pre_run_cmds": False,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800429 "run_cmd": False,
Srikrishna Iyer6c531b02020-11-23 18:41:16 -0800430 "run_opts": False,
431 "post_run_cmds": False,
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700432 })
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800433
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700434 self.mandatory_misc_attrs.update({
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800435 "run_dir_name": False,
Srikrishna Iyer1f5fae92021-02-18 21:57:25 -0800436 "cov_db_dir": False,
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800437 "cov_db_test_dir": False,
Weicai Yang31029d22020-03-24 11:14:56 -0700438 "run_pass_patterns": False,
439 "run_fail_patterns": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700440 })
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800441
Srikrishna Iyer65783742021-02-10 21:42:12 -0800442 def _set_attrs(self):
443 super()._extract_attrs(self.test_obj.__dict__)
444 super()._set_attrs()
Rupert Swarbrick381770d2020-10-12 08:50:08 +0100445
Srikrishna Iyer65783742021-02-10 21:42:12 -0800446 # 'test' is used as a substitution variable in the HJson.
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800447 self.test = self.name
Srikrishna Iyer65783742021-02-10 21:42:12 -0800448 self.build_mode = self.test_obj.build_mode.name
449 self.qual_name = self.run_dir_name + "." + str(self.seed)
450 self.full_name = self.sim_cfg.name + ":" + self.qual_name
Srikrishna Iyer956b3f82021-04-15 15:14:56 -0700451 self.job_name += f"_{self.build_mode}"
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700452 if self.sim_cfg.cov:
Cindy Chend5977582021-06-05 12:49:10 -0700453 self.output_dirs += [self.cov_db_dir]
Srikrishna Iyerd37d10d2021-03-25 18:35:36 -0700454
455 # In GUI mode, the log file is not updated; hence, nothing to check.
Srikrishna Iyerd5623772021-06-18 22:23:07 -0700456 if not self.gui:
Srikrishna Iyerd37d10d2021-03-25 18:35:36 -0700457 self.pass_patterns = self.run_pass_patterns
458 self.fail_patterns = self.run_fail_patterns
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800459
Srikrishna Iyerd5623772021-06-18 22:23:07 -0700460 def pre_launch(self):
461 self.launcher.renew_odir = True
462
Srikrishna Iyer65783742021-02-10 21:42:12 -0800463 def post_finish(self, status):
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100464 if status != 'P':
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800465 # Delete the coverage data if available.
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800466 rm_path(self.cov_db_test_dir)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800467
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800468 @staticmethod
469 def get_seed():
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700470 # If --seeds option is passed, then those custom seeds are consumed
471 # first. If --fixed-seed <val> is also passed, the subsequent tests
472 # (once the custom seeds are consumed) will be run with the fixed seed.
Philipp Wagner9a521782020-02-25 11:49:53 +0000473 if not RunTest.seeds:
Rupert Swarbrick6cc20112020-04-24 09:44:35 +0100474 if RunTest.fixed_seed:
475 return RunTest.fixed_seed
Srikrishna Iyera821bbc2020-01-31 00:28:02 -0800476 for i in range(1000):
Philipp Wagner75fd6c72020-02-25 11:47:41 +0000477 seed = random.getrandbits(32)
Srikrishna Iyera821bbc2020-01-31 00:28:02 -0800478 RunTest.seeds.append(seed)
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800479 return RunTest.seeds.pop(0)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800480
481
Weicai Yang080632d2020-10-16 17:52:14 -0700482class CovUnr(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800483 """Abstraction for coverage UNR flow."""
Weicai Yang080632d2020-10-16 17:52:14 -0700484
Srikrishna Iyer65783742021-02-10 21:42:12 -0800485 target = "cov_unr"
Weicai Yang080632d2020-10-16 17:52:14 -0700486
487 def __init__(self, sim_cfg):
Weicai Yang080632d2020-10-16 17:52:14 -0700488 super().__init__(sim_cfg)
489
Srikrishna Iyer65783742021-02-10 21:42:12 -0800490 def _define_attrs(self):
491 super()._define_attrs()
Weicai Yang080632d2020-10-16 17:52:14 -0700492 self.mandatory_cmd_attrs.update({
493 # tool srcs
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800494 "proj_root": False,
Weicai Yang080632d2020-10-16 17:52:14 -0700495
496 # Need to generate filelist based on build mode
497 "sv_flist_gen_cmd": False,
498 "sv_flist_gen_dir": False,
499 "sv_flist_gen_opts": False,
500 "build_dir": False,
501 "cov_unr_build_cmd": False,
502 "cov_unr_build_opts": False,
503 "cov_unr_run_cmd": False,
504 "cov_unr_run_opts": False
505 })
506
507 self.mandatory_misc_attrs.update({
508 "cov_unr_dir": False,
Srikrishna Iyer8e21e512021-04-02 01:55:47 -0700509 "cov_merge_db_dir": False,
Weicai Yang080632d2020-10-16 17:52:14 -0700510 "build_fail_patterns": False
511 })
512
Srikrishna Iyer65783742021-02-10 21:42:12 -0800513 def _set_attrs(self):
514 super()._set_attrs()
515 self.qual_name = self.target
516 self.full_name = self.sim_cfg.name + ":" + self.qual_name
Srikrishna Iyer8e21e512021-04-02 01:55:47 -0700517 self.input_dirs += [self.cov_merge_db_dir]
Weicai Yang080632d2020-10-16 17:52:14 -0700518
Srikrishna Iyer65783742021-02-10 21:42:12 -0800519 # Reuse the build_fail_patterns set in the HJson.
Weicai Yang080632d2020-10-16 17:52:14 -0700520 self.fail_patterns = self.build_fail_patterns
521
Weicai Yang080632d2020-10-16 17:52:14 -0700522
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800523class CovMerge(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800524 """Abstraction for merging coverage databases."""
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800525
Srikrishna Iyer65783742021-02-10 21:42:12 -0800526 target = "cov_merge"
Srikrishna Iyer1d2899f2021-02-11 18:23:15 -0800527 weight = 10
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800528
Rupert Swarbrickf2bf3702020-10-12 17:04:46 +0100529 def __init__(self, run_items, sim_cfg):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800530 # Construct the cov_db_dirs right away from the run_items. This is a
531 # special variable used in the HJson.
532 self.cov_db_dirs = []
533 for run in run_items:
534 if run.cov_db_dir not in self.cov_db_dirs:
535 self.cov_db_dirs.append(run.cov_db_dir)
536
Srikrishna Iyercaa0d892021-03-01 13:15:52 -0800537 # Early lookup the cov_merge_db_dir, which is a mandatory misc
538 # attribute anyway. We need it to compute additional cov db dirs.
539 self.cov_merge_db_dir = subst_wildcards("{cov_merge_db_dir}",
540 sim_cfg.__dict__)
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700541
Srikrishna Iyercaa0d892021-03-01 13:15:52 -0800542 # Prune previous merged cov directories, keeping past 7 dbs.
543 prev_cov_db_dirs = clean_odirs(odir=self.cov_merge_db_dir, max_odirs=7)
544
545 # If the --cov-merge-previous command line switch is passed, then
546 # merge coverage with the previous runs.
547 if sim_cfg.cov_merge_previous:
548 self.cov_db_dirs += [str(item) for item in prev_cov_db_dirs]
549
550 super().__init__(sim_cfg)
Rupert Swarbrickf2bf3702020-10-12 17:04:46 +0100551 self.dependencies += run_items
Srikrishna Iyer65783742021-02-10 21:42:12 -0800552 # Run coverage merge even if one test passes.
Cindy Chen924c6062021-01-27 14:27:50 -0800553 self.needs_all_dependencies_passing = False
Rupert Swarbrickf2bf3702020-10-12 17:04:46 +0100554
Srikrishna Iyer65783742021-02-10 21:42:12 -0800555 # Append cov_db_dirs to the list of exports.
Srikrishna Iyer37b0c992021-03-23 16:27:26 -0700556 self.exports["cov_db_dirs"] = shlex.quote(" ".join(self.cov_db_dirs))
Srikrishna Iyer65783742021-02-10 21:42:12 -0800557
558 def _define_attrs(self):
559 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700560 self.mandatory_cmd_attrs.update({
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800561 "cov_merge_cmd": False,
562 "cov_merge_opts": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700563 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800564
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700565 self.mandatory_misc_attrs.update({
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800566 "cov_merge_dir": False,
567 "cov_merge_db_dir": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700568 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800569
Srikrishna Iyer65783742021-02-10 21:42:12 -0800570 def _set_attrs(self):
571 super()._set_attrs()
572 self.qual_name = self.target
573 self.full_name = self.sim_cfg.name + ":" + self.qual_name
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800574
Srikrishna Iyer65783742021-02-10 21:42:12 -0800575 # For merging coverage db, the precise output dir is set in the HJson.
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800576 self.odir = self.cov_merge_db_dir
Srikrishna Iyer37b0c992021-03-23 16:27:26 -0700577 self.input_dirs += self.cov_db_dirs
Srikrishna Iyer08ccc232021-03-31 00:01:54 -0700578 self.output_dirs = [self.odir]
Srikrishna Iyer37b0c992021-03-23 16:27:26 -0700579
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800580
581class CovReport(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800582 """Abstraction for coverage report generation. """
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800583
Srikrishna Iyer65783742021-02-10 21:42:12 -0800584 target = "cov_report"
Srikrishna Iyer1d2899f2021-02-11 18:23:15 -0800585 weight = 10
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800586
Rupert Swarbrick381770d2020-10-12 08:50:08 +0100587 def __init__(self, merge_job, sim_cfg):
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700588 super().__init__(sim_cfg)
Rupert Swarbrick381770d2020-10-12 08:50:08 +0100589 self.dependencies.append(merge_job)
590
Srikrishna Iyer65783742021-02-10 21:42:12 -0800591 def _define_attrs(self):
592 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700593 self.mandatory_cmd_attrs.update({
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800594 "cov_report_cmd": False,
595 "cov_report_opts": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700596 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800597
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700598 self.mandatory_misc_attrs.update({
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800599 "cov_report_dir": False,
600 "cov_merge_db_dir": False,
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700601 "cov_report_txt": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700602 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800603
Srikrishna Iyer65783742021-02-10 21:42:12 -0800604 def _set_attrs(self):
605 super()._set_attrs()
606 self.qual_name = self.target
607 self.full_name = self.sim_cfg.name + ":" + self.qual_name
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800608
Srikrishna Iyer65783742021-02-10 21:42:12 -0800609 # Keep track of coverage results, once the job is finished.
610 self.cov_total = ""
611 self.cov_results = ""
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800612
Srikrishna Iyer65783742021-02-10 21:42:12 -0800613 def post_finish(self, status):
614 """Extract the coverage results summary for the dashboard.
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800615
Srikrishna Iyer9b381412021-08-10 23:11:43 -0700616 If the extraction fails, an appropriate exception is raised, which must
617 be caught by the caller to mark the job as a failure.
Srikrishna Iyer65783742021-02-10 21:42:12 -0800618 """
619
620 if self.dry_run or status != 'P':
621 return
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800622
Srikrishna Iyer9b381412021-08-10 23:11:43 -0700623 results, self.cov_total = get_cov_summary_table(
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100624 self.cov_report_txt, self.sim_cfg.tool)
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800625
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100626 colalign = (("center", ) * len(results[0]))
627 self.cov_results = tabulate(results,
628 headers="firstrow",
629 tablefmt="pipe",
630 colalign=colalign)
631
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800632
633class CovAnalyze(Deploy):
Srikrishna Iyer65783742021-02-10 21:42:12 -0800634 """Abstraction for running the coverage analysis tool."""
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800635
Srikrishna Iyer65783742021-02-10 21:42:12 -0800636 target = "cov_analyze"
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800637
638 def __init__(self, sim_cfg):
Srikrishna Iyer8e21e512021-04-02 01:55:47 -0700639 # Enforce GUI mode for coverage analysis.
640 sim_cfg.gui = True
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700641 super().__init__(sim_cfg)
642
Srikrishna Iyer65783742021-02-10 21:42:12 -0800643 def _define_attrs(self):
644 super()._define_attrs()
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700645 self.mandatory_cmd_attrs.update({
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700646 # tool srcs
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800647 "proj_root": False,
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800648 "cov_analyze_cmd": False,
649 "cov_analyze_opts": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700650 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800651
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700652 self.mandatory_misc_attrs.update({
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800653 "cov_analyze_dir": False,
654 "cov_merge_db_dir": False
Srikrishna Iyere4662af2020-08-10 13:19:42 -0700655 })
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800656
Srikrishna Iyer65783742021-02-10 21:42:12 -0800657 def _set_attrs(self):
658 super()._set_attrs()
659 self.qual_name = self.target
660 self.full_name = self.sim_cfg.name + ":" + self.qual_name
Srikrishna Iyer8e21e512021-04-02 01:55:47 -0700661 self.input_dirs += [self.cov_merge_db_dir]