blob: 7f2fdea75d458d3353da9c1651c0b3753eea1b27 [file] [log] [blame]
Srikrishna Iyer09a81e92019-12-30 10:47:57 -08001#!/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
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +01005"""dvsim is a command line tool to deploy ASIC tool flows such as regressions
6for design verification (DV), formal property verification (FPV), linting and
7synthesis.
8
9It uses hjson as the format for specifying what to build and run. It is an
10end-to-end regression manager that can deploy multiple builds (where some tests
11might need different set of compile time options requiring a uniquely build sim
12executable) in parallel followed by tests in parallel using the load balancer
13of your choice.
14
15dvsim is built to be tool-agnostic so that you can easily switch between the
16tools at your disposal. dvsim uses fusesoc as the starting step to resolve all
17inter-package dependencies and provide us with a filelist that will be consumed
18by the sim tool.
19
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080020"""
21
22import argparse
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080023import datetime
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080024import logging as log
25import os
Cindy Chenec6449e2020-12-17 11:55:38 -080026import shutil
Eunchan Kim87e8f852021-01-05 09:03:01 -080027import shlex
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080028import subprocess
29import sys
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +010030import textwrap
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080031
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070032import Deploy
Rupert Swarbrickc0e753d2020-06-04 13:27:58 +010033from Scheduler import Scheduler
34from Timer import Timer
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070035import utils
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080036from CfgFactory import make_cfg
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080037
38# TODO: add dvsim_cfg.hjson to retrieve this info
39version = 0.1
40
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080041# By default, all build and run artifacts go here.
42DEFAULT_SCRATCH_ROOT = os.getcwd() + "/scratch"
43
Rupert Swarbricke83b55e2020-05-12 11:44:04 +010044# The different categories that can be passed to the --list argument.
45_LIST_CATEGORIES = ["build_modes", "run_modes", "tests", "regressions"]
46
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080047
48# Function to resolve the scratch root directory among the available options:
49# If set on the command line, then use that as a preference.
50# Else, check if $SCRATCH_ROOT env variable exists and is a directory.
51# Else use the default (<cwd>/scratch)
52# Try to create the directory if it does not already exist.
53def resolve_scratch_root(arg_scratch_root):
54 scratch_root = os.environ.get('SCRATCH_ROOT')
Rupert Swarbrick06383682020-03-24 15:56:41 +000055 if not arg_scratch_root:
56 if scratch_root is None:
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080057 arg_scratch_root = DEFAULT_SCRATCH_ROOT
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080058 else:
59 # Scratch space could be mounted in a filesystem (such as NFS) on a network drive.
60 # If the network is down, it could cause the access access check to hang. So run a
61 # simple ls command with a timeout to prevent the hang.
62 (out,
63 status) = utils.run_cmd_with_timeout(cmd="ls -d " + scratch_root,
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080064 timeout=1,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080065 exit_on_failure=0)
66 if status == 0 and out != "":
67 arg_scratch_root = scratch_root
68 else:
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080069 arg_scratch_root = DEFAULT_SCRATCH_ROOT
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080070 log.warning(
Rupert Swarbrick06383682020-03-24 15:56:41 +000071 "Env variable $SCRATCH_ROOT=\"{}\" is not accessible.\n"
72 "Using \"{}\" instead.".format(scratch_root,
73 arg_scratch_root))
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080074 else:
75 arg_scratch_root = os.path.realpath(arg_scratch_root)
76
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080077 try:
78 os.system("mkdir -p " + arg_scratch_root)
Rupert Swarbrick06383682020-03-24 15:56:41 +000079 except OSError:
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080080 log.fatal(
81 "Invalid --scratch-root=\"%s\" switch - failed to create directory!",
82 arg_scratch_root)
83 sys.exit(1)
84 return (arg_scratch_root)
85
86
Rupert Swarbrick536ab202020-06-09 16:37:12 +010087def read_max_parallel(arg):
88 '''Take value for --max-parallel as an integer'''
89 try:
90 int_val = int(arg)
91 if int_val <= 0:
92 raise ValueError('bad value')
93 return int_val
94
95 except ValueError:
Weicai Yang813d6892020-10-19 16:20:31 -070096 raise argparse.ArgumentTypeError(
97 'Bad argument for --max-parallel '
98 '({!r}): must be a positive integer.'.format(arg))
Rupert Swarbrick536ab202020-06-09 16:37:12 +010099
100
101def resolve_max_parallel(arg):
102 '''Pick a value of max_parallel, defaulting to 16 or $DVSIM_MAX_PARALLEL'''
103 if arg is not None:
104 assert arg > 0
105 return arg
106
107 from_env = os.environ.get('DVSIM_MAX_PARALLEL')
108 if from_env is not None:
109 try:
110 return read_max_parallel(from_env)
111 except argparse.ArgumentTypeError:
112 log.warning('DVSIM_MAX_PARALLEL environment variable has value '
113 '{!r}, which is not a positive integer. Using default '
Weicai Yang813d6892020-10-19 16:20:31 -0700114 'value (16).'.format(from_env))
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100115
116 return 16
117
118
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100119def resolve_branch(branch):
120 '''Choose a branch name for output files
121
122 If the --branch argument was passed on the command line, the branch
123 argument is the branch name to use. Otherwise it is None and we use git to
124 find the name of the current branch in the working directory.
125
126 '''
127
128 if branch is not None:
129 return branch
130
131 result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"],
132 stdout=subprocess.PIPE)
133 branch = result.stdout.decode("utf-8").strip()
134 if not branch:
135 log.warning("Failed to find current git branch. "
136 "Setting it to \"default\"")
137 branch = "default"
138
139 return branch
140
141
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800142# Get the project root directory path - this is used to construct the full paths
143def get_proj_root():
Rupert Swarbrick06383682020-03-24 15:56:41 +0000144 cmd = ["git", "rev-parse", "--show-toplevel"]
145 result = subprocess.run(cmd,
Rupert Swarbrick1dd327bd2020-03-24 15:14:00 +0000146 stdout=subprocess.PIPE,
147 stderr=subprocess.PIPE)
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700148 proj_root = result.stdout.decode("utf-8").strip()
Rupert Swarbrick06383682020-03-24 15:56:41 +0000149 if not proj_root:
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700150 log.error(
Rupert Swarbrick06383682020-03-24 15:56:41 +0000151 "Attempted to find the root of this GitHub repository by running:\n"
152 "{}\n"
153 "But this command has failed:\n"
154 "{}".format(' '.join(cmd), result.stderr.decode("utf-8")))
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700155 sys.exit(1)
156 return (proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800157
158
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800159def resolve_proj_root(args):
160 '''Update proj_root based on how DVSim is invoked.
161
162 If --remote env var is set, a location in the scratch area is chosen as the
163 new proj_root. The entire repo is copied over to this location. Else, the
164 proj_root is discovered using get_proj_root() method, unless the user
165 overrides it on the command line.
166
Cindy Chen13a5dde2020-12-21 09:31:56 -0800167 This function returns the updated proj_root src and destination path. If
168 --remote env var is not set, the destination path is identical to the src path.
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800169 '''
Cindy Chen13a5dde2020-12-21 09:31:56 -0800170 proj_root_src = args.proj_root or get_proj_root()
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800171
172 # Check if jobs are dispatched to external compute machines. If yes,
173 # then the repo needs to be copied over to the scratch area
174 # accessible to those machines.
Cindy Chenec6449e2020-12-17 11:55:38 -0800175 # If --purge arg is set, then purge the repo_top that was copied before.
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800176 if args.remote:
Cindy Chen13a5dde2020-12-21 09:31:56 -0800177 proj_root_dest = os.path.join(args.scratch_root, args.branch,
178 "repo_top")
Srikrishna Iyerab4327e2020-12-29 12:49:07 -0800179 if args.purge:
180 shutil.rmtree(proj_root_dest, ignore_errors=True)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800181 copy_repo(proj_root_src, proj_root_dest, args.dry_run)
182 else:
183 proj_root_dest = proj_root_src
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800184
Cindy Chen13a5dde2020-12-21 09:31:56 -0800185 return proj_root_src, proj_root_dest
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800186
187
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800188def copy_repo(src, dest, dry_run):
189 '''Copy over the repo to a new location.
190
191 The repo is copied over from src to dest area. It tentatively uses the
192 rsync utility which provides the ability to specify a file containing some
193 exclude patterns to skip certain things from being copied over. With GitHub
194 repos, an existing `.gitignore` serves this purpose pretty well.
195 '''
Eunchan Kim425a0b82021-01-06 09:25:10 -0800196 rsync_cmd = ["rsync",
197 "--recursive", "--links", "--checksum", "--update",
198 "--inplace", "--no-group"]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800199
200 # Supply `.gitignore` from the src area to skip temp files.
201 ignore_patterns_file = os.path.join(src, ".gitignore")
202 if os.path.exists(ignore_patterns_file):
203 # TODO: hack - include hw/foundry since it is excluded in .gitignore.
Eunchan Kim425a0b82021-01-06 09:25:10 -0800204 rsync_cmd += ["--include=hw/foundry",
205 "--exclude-from={}".format(ignore_patterns_file),
206 "--exclude=.*"]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800207
Eunchan Kim425a0b82021-01-06 09:25:10 -0800208 rsync_cmd += [src + "/.", dest]
209 rsync_str = ' '.join([shlex.quote(w) for w in rsync_cmd])
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800210
Eunchan Kim425a0b82021-01-06 09:25:10 -0800211 cmd = ["flock", "--timeout", "600", dest, "--command", rsync_str]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800212
213 log.info("[copy_repo] [dest]: %s", dest)
214 log.log(utils.VERBOSE, "[copy_repo] [cmd]: \n%s", ' '.join(cmd))
215 if not dry_run:
216 # Make sure the dest exists first.
217 os.makedirs(dest, exist_ok=True)
218 try:
Eunchan Kim87e8f852021-01-05 09:03:01 -0800219 subprocess.run(cmd,
220 check=True,
221 stdout=subprocess.PIPE,
222 stderr=subprocess.PIPE)
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800223 except subprocess.CalledProcessError as e:
224 log.error("Failed to copy over %s to %s: %s", src, dest,
225 e.stderr.decode("utf-8").strip())
226 log.info("Done.")
227
228
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100229def wrapped_docstring():
230 '''Return a text-wrapped version of the module docstring'''
231 paras = []
232 para = []
233 for line in __doc__.strip().split('\n'):
234 line = line.strip()
235 if not line:
236 if para:
237 paras.append('\n'.join(para))
238 para = []
239 else:
240 para.append(line)
241 if para:
242 paras.append('\n'.join(para))
243
244 return '\n\n'.join(textwrap.fill(p) for p in paras)
245
246
247def parse_args():
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800248 parser = argparse.ArgumentParser(
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100249 description=wrapped_docstring(),
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800250 formatter_class=argparse.RawDescriptionHelpFormatter)
251
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800252 parser.add_argument("cfg",
253 metavar="<cfg-hjson-file>",
254 help="""Configuration hjson file.""")
255
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800256 parser.add_argument("--version",
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800257 action='store_true',
258 help="Print version and exit")
259
Weicai Yang813d6892020-10-19 16:20:31 -0700260 parser.add_argument(
261 "--tool",
262 "-t",
263 help=("Explicitly set the tool to use. This is "
264 "optional for running simulations (where it can "
265 "be set in an .hjson file), but is required for "
266 "other flows. Possible tools include: vcs, "
267 "xcelium, ascentlint, veriblelint, verilator, dc."))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800268
Weicai Yang813d6892020-10-19 16:20:31 -0700269 parser.add_argument("--list",
270 "-l",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100271 nargs="*",
272 metavar='CAT',
273 choices=_LIST_CATEGORIES,
274 help=('Parse the the given .hjson config file, list '
275 'the things that can be run, then exit. The '
276 'list can be filtered with a space-separated '
Weicai Yang813d6892020-10-19 16:20:31 -0700277 'of categories from: {}.'.format(
278 ', '.join(_LIST_CATEGORIES))))
Srikrishna Iyer64009052020-01-13 11:27:39 -0800279
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100280 whatg = parser.add_argument_group('Choosing what to run')
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800281
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100282 whatg.add_argument("-i",
283 "--items",
284 nargs="*",
Cindy Chene513e362020-11-11 10:28:54 -0800285 default=["smoke"],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100286 help=('Specify the regressions or tests to run. '
Cindy Chene513e362020-11-11 10:28:54 -0800287 'Defaults to "smoke", but can be a '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100288 'space separated list of test or regression '
289 'names.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800290
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100291 whatg.add_argument("--select-cfgs",
292 nargs="*",
293 metavar="CFG",
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700294 help=('The .hjson file is a primary config. Only run '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100295 'the given configs from it. If this argument is '
296 'not used, dvsim will process all configs listed '
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700297 'in a primary config.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800298
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100299 disg = parser.add_argument_group('Dispatch options')
300
301 disg.add_argument("--job-prefix",
302 default="",
303 metavar="PFX",
304 help=('Prepend this string when running each tool '
305 'command.'))
306
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800307 disg.add_argument("--remote",
308 action='store_true',
309 help=('Trigger copying of the repo to scratch area.'))
310
Weicai Yang813d6892020-10-19 16:20:31 -0700311 disg.add_argument("--max-parallel",
312 "-mp",
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100313 type=read_max_parallel,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100314 metavar="N",
315 help=('Run only up to N builds/tests at a time. '
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100316 'Default value 16, unless the DVSIM_MAX_PARALLEL '
317 'environment variable is set, in which case that '
318 'is used.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100319
320 pathg = parser.add_argument_group('File management')
321
Weicai Yang813d6892020-10-19 16:20:31 -0700322 pathg.add_argument("--scratch-root",
323 "-sr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100324 metavar="PATH",
325 help=('Destination for build / run directories. If not '
326 'specified, uses the path in the SCRATCH_ROOT '
327 'environment variable, if set, or ./scratch '
328 'otherwise.'))
329
Weicai Yang813d6892020-10-19 16:20:31 -0700330 pathg.add_argument("--proj-root",
331 "-pr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100332 metavar="PATH",
333 help=('The root directory of the project. If not '
334 'specified, dvsim will search for a git '
335 'repository containing the current directory.'))
336
Weicai Yang813d6892020-10-19 16:20:31 -0700337 pathg.add_argument("--branch",
338 "-br",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100339 metavar='B',
340 help=('By default, dvsim creates files below '
341 '{scratch-root}/{dut}.{flow}.{tool}/{branch}. '
342 'If --branch is not specified, dvsim assumes the '
343 'current directory is a git repository and uses '
344 'the name of the current branch.'))
345
Weicai Yang813d6892020-10-19 16:20:31 -0700346 pathg.add_argument("--max-odirs",
347 "-mo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100348 type=int,
349 default=5,
350 metavar="N",
351 help=('When tests are run, older runs are backed '
352 'up. Discard all but the N most recent (defaults '
353 'to 5).'))
354
355 pathg.add_argument("--purge",
356 action='store_true',
357 help="Clean the scratch directory before running.")
358
359 buildg = parser.add_argument_group('Options for building')
360
361 buildg.add_argument("--build-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800362 "-bu",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100363 action='store_true',
364 help=('Stop after building executables for the given '
365 'items.'))
366
Weicai Yang813d6892020-10-19 16:20:31 -0700367 buildg.add_argument("--build-unique",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100368 action='store_true',
369 help=('Append a timestamp to the directory in which '
370 'files are built. This is suitable for the case '
371 'when another test is already running and you '
372 'want to run something else from a different '
373 'terminal without affecting it.'))
374
Weicai Yang813d6892020-10-19 16:20:31 -0700375 buildg.add_argument("--build-opts",
376 "-bo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100377 nargs="+",
378 default=[],
379 metavar="OPT",
380 help=('Additional options passed on the command line '
381 'each time a build tool is run.'))
382
Weicai Yang813d6892020-10-19 16:20:31 -0700383 buildg.add_argument("--build-modes",
384 "-bm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100385 nargs="+",
386 default=[],
387 metavar="MODE",
388 help=('The options for each build_mode in this list '
389 'are applied to all build and run targets.'))
390
391 rung = parser.add_argument_group('Options for running')
392
393 rung.add_argument("--run-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800394 "-ru",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100395 action='store_true',
396 help=('Skip the build step (assume that simulation '
397 'executables have already been built).'))
398
Weicai Yang813d6892020-10-19 16:20:31 -0700399 rung.add_argument("--run-opts",
400 "-ro",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100401 nargs="+",
402 default=[],
403 metavar="OPT",
404 help=('Additional options passed on the command line '
405 'each time a test is run.'))
406
Weicai Yang813d6892020-10-19 16:20:31 -0700407 rung.add_argument("--run-modes",
408 "-rm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100409 nargs="+",
410 default=[],
411 metavar="MODE",
412 help=('The options for each run_mode in this list are '
413 'applied to each simulation run.'))
414
Weicai Yang813d6892020-10-19 16:20:31 -0700415 rung.add_argument("--profile",
416 "-p",
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800417 nargs="?",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100418 choices=['time', 'mem'],
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800419 const="time",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100420 metavar="P",
421 help=('Turn on simulation profiling (where P is time '
422 'or mem).'))
423
424 rung.add_argument("--xprop-off",
425 action='store_true',
426 help="Turn off X-propagation in simulation.")
427
428 rung.add_argument("--no-rerun",
429 action='store_true',
430 help=("Disable the default behaviour, where failing "
431 "tests are automatically rerun with waves "
432 "enabled."))
433
Weicai Yang813d6892020-10-19 16:20:31 -0700434 rung.add_argument("--verbosity",
435 "-v",
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800436 choices=['n', 'l', 'm', 'h', 'f', 'd'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100437 metavar='V',
Rupert Swarbrickdcec9942020-09-04 08:15:07 +0100438 help=('Set tool/simulation verbosity to none (n), low '
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800439 '(l), medium (m), high (h), full (f) or debug (d).'
440 ' The default value is set in config files.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100441
442 seedg = parser.add_argument_group('Test seeds')
443
Weicai Yang813d6892020-10-19 16:20:31 -0700444 seedg.add_argument("--seeds",
445 "-s",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100446 nargs="+",
447 default=[],
448 metavar="S",
449 help=('A list of seeds for tests. Note that these '
450 'specific seeds are applied to items being run '
451 'in the order they are passed.'))
452
453 seedg.add_argument("--fixed-seed",
454 type=int,
455 metavar='S',
456 help=('Run all items with the seed S. This implies '
457 '--reseed 1.'))
458
Weicai Yang813d6892020-10-19 16:20:31 -0700459 seedg.add_argument("--reseed",
460 "-r",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100461 type=int,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100462 metavar="N",
463 help=('Override any reseed value in the test '
464 'configuration and run each test N times, with '
465 'a new seed each time.'))
466
Weicai Yang813d6892020-10-19 16:20:31 -0700467 seedg.add_argument("--reseed-multiplier",
468 "-rx",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100469 type=int,
470 default=1,
471 metavar="N",
472 help=('Scale each reseed value in the test '
473 'configuration by N. This allows e.g. running '
474 'the tests 10 times as much as normal while '
475 'maintaining the ratio of numbers of runs '
476 'between different tests.'))
477
478 waveg = parser.add_argument_group('Dumping waves')
479
Weicai Yang813d6892020-10-19 16:20:31 -0700480 waveg.add_argument(
481 "--waves",
482 "-w",
483 nargs="?",
484 choices=["default", "fsdb", "shm", "vpd", "vcd", "evcd", "fst"],
485 const="default",
486 help=("Enable dumping of waves. It takes an optional "
487 "argument to pick the desired wave format. If "
488 "the optional argument is not supplied, it picks "
489 "whatever is the default for the chosen tool. "
490 "By default, dumping waves is not enabled."))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100491
Weicai Yang813d6892020-10-19 16:20:31 -0700492 waveg.add_argument("--max-waves",
493 "-mw",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100494 type=int,
495 default=5,
496 metavar="N",
497 help=('Only dump waves for the first N tests run. This '
498 'includes both tests scheduled for run and those '
499 'that are automatically rerun.'))
500
501 covg = parser.add_argument_group('Generating simulation coverage')
502
Weicai Yang813d6892020-10-19 16:20:31 -0700503 covg.add_argument("--cov",
504 "-c",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100505 action='store_true',
506 help="Enable collection of coverage data.")
507
508 covg.add_argument("--cov-merge-previous",
509 action='store_true',
510 help=('Only applicable with --cov. Merge any previous '
511 'coverage database directory with the new '
512 'coverage database.'))
513
Weicai Yang080632d2020-10-16 17:52:14 -0700514 covg.add_argument("--cov-unr",
515 action='store_true',
516 help=('Run coverage UNR analysis and generate report. '
517 'This only supports VCS now.'))
518
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100519 covg.add_argument("--cov-analyze",
520 action='store_true',
521 help=('Rather than building or running any tests, '
522 'analyze the coverage from the last run.'))
523
524 pubg = parser.add_argument_group('Generating and publishing results')
525
526 pubg.add_argument("--map-full-testplan",
527 action='store_true',
528 help=("Show complete testplan annotated results "
529 "at the end."))
530
531 pubg.add_argument("--publish",
532 action='store_true',
533 help="Publish results to reports.opentitan.org.")
534
535 dvg = parser.add_argument_group('Controlling DVSim itself')
536
Weicai Yang813d6892020-10-19 16:20:31 -0700537 dvg.add_argument("--print-interval",
538 "-pi",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100539 type=int,
540 default=10,
541 metavar="N",
542 help="Print status every N seconds.")
543
544 dvg.add_argument("--verbose",
545 nargs="?",
546 choices=['default', 'debug'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100547 const="default",
548 metavar="D",
549 help=('With no argument, print verbose dvsim tool '
550 'messages. With --verbose=debug, the volume of '
551 'messages is even higher.'))
552
Weicai Yang813d6892020-10-19 16:20:31 -0700553 dvg.add_argument("--dry-run",
554 "-n",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100555 action='store_true',
556 help=("Print dvsim tool messages but don't actually "
557 "run any command"))
558
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100559 args = parser.parse_args()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800560
561 if args.version:
562 print(version)
563 sys.exit()
564
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100565 # We want the --list argument to default to "all categories", but allow
566 # filtering. If args.list is None, then --list wasn't supplied. If it is
567 # [], then --list was supplied with no further arguments and we want to
568 # list all categories.
569 if args.list == []:
570 args.list = _LIST_CATEGORIES
571
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100572 # Get max_parallel from environment if it wasn't specified on the command
573 # line.
574 args.max_parallel = resolve_max_parallel(args.max_parallel)
575 assert args.max_parallel > 0
576
577 return args
578
579
580def main():
581 args = parse_args()
582
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800583 # Add log level 'VERBOSE' between INFO and DEBUG
584 log.addLevelName(utils.VERBOSE, 'VERBOSE')
585
586 log_format = '%(levelname)s: [%(module)s] %(message)s'
587 log_level = log.INFO
Rupert Swarbrick06383682020-03-24 15:56:41 +0000588 if args.verbose == "default":
589 log_level = utils.VERBOSE
590 elif args.verbose == "debug":
591 log_level = log.DEBUG
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800592 log.basicConfig(format=log_format, level=log_level)
593
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800594 if not os.path.exists(args.cfg):
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800595 log.fatal("Path to config file %s appears to be invalid.", args.cfg)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800596 sys.exit(1)
597
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800598 # If publishing results, then force full testplan mapping of results.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000599 if args.publish:
600 args.map_full_testplan = True
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800601
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800602 args.scratch_root = resolve_scratch_root(args.scratch_root)
603 args.branch = resolve_branch(args.branch)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800604 proj_root_src, proj_root = resolve_proj_root(args)
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800605 log.info("[proj_root]: %s", proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800606
Cindy Chen13a5dde2020-12-21 09:31:56 -0800607 args.cfg = os.path.abspath(args.cfg)
608 if args.remote:
609 cfg_path = args.cfg.replace(proj_root_src + "/", "")
610 args.cfg = os.path.join(proj_root, cfg_path)
611
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800612 # Add timestamp to args that all downstream objects can use.
613 # Static variables - indicate timestamp.
Eunchan Kim46125cd2020-04-09 09:26:52 -0700614 ts_format_long = "%A %B %d %Y %I:%M:%S%p UTC"
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800615 ts_format = "%a.%m.%d.%y__%I.%M.%S%p"
Eunchan Kim46125cd2020-04-09 09:26:52 -0700616 curr_ts = datetime.datetime.utcnow()
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800617 timestamp_long = curr_ts.strftime(ts_format_long)
618 timestamp = curr_ts.strftime(ts_format)
619 setattr(args, "ts_format_long", ts_format_long)
620 setattr(args, "ts_format", ts_format)
621 setattr(args, "timestamp_long", timestamp_long)
622 setattr(args, "timestamp", timestamp)
623
624 # Register the seeds from command line with RunTest class.
625 Deploy.RunTest.seeds = args.seeds
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700626 # If we are fixing a seed value, no point in tests having multiple reseeds.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000627 if args.fixed_seed:
628 args.reseed = 1
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700629 Deploy.RunTest.fixed_seed = args.fixed_seed
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800630
631 # Register the common deploy settings.
Rupert Swarbrickc0e753d2020-06-04 13:27:58 +0100632 Timer.print_interval = args.print_interval
633 Scheduler.max_parallel = args.max_parallel
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800634 Deploy.Deploy.max_odirs = args.max_odirs
635
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800636 # Build infrastructure from hjson file and create the list of items to
637 # be deployed.
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700638 global cfg
Rupert Swarbricka23dfec2020-09-07 10:01:28 +0100639 cfg = make_cfg(args.cfg, args, proj_root)
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100640
Srikrishna Iyer64009052020-01-13 11:27:39 -0800641 # List items available for run if --list switch is passed, and exit.
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100642 if args.list is not None:
Srikrishna Iyer64009052020-01-13 11:27:39 -0800643 cfg.print_list()
644 sys.exit(0)
645
Weicai Yang080632d2020-10-16 17:52:14 -0700646 # Purge the scratch path if --purge option is set.
647 if args.purge:
648 cfg.purge()
649
650 # If --cov-unr is passed, run UNR to generate report for unreachable
651 # exclusion file.
652 if args.cov_unr:
653 cfg.cov_unr()
654 cfg.deploy_objects()
655 sys.exit(0)
656
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800657 # In simulation mode: if --cov-analyze switch is passed, then run the GUI
658 # tool.
659 if args.cov_analyze:
660 cfg.cov_analyze()
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700661 cfg.deploy_objects()
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800662 sys.exit(0)
663
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800664 # Deploy the builds and runs
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800665 if args.items != []:
666 # Create deploy objects.
667 cfg.create_deploy_objects()
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100668 results = cfg.deploy_objects()
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800669
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800670 # Generate results.
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100671 cfg.gen_results(results)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800672
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800673 # Publish results
Rupert Swarbrick06383682020-03-24 15:56:41 +0000674 if args.publish:
675 cfg.publish_results()
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800676
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800677 else:
678 log.info("No items specified to be run.")
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800679
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800680 # Exit with non-zero status if there were errors or failures.
681 if cfg.has_errors():
682 log.error("Errors were encountered in this run.")
683 sys.exit(1)
684
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800685
686if __name__ == '__main__':
687 main()