blob: a992a4aeb4eb63171677c888495fdaff5032ca69 [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
Weicai Yangfc2ff3b2020-03-19 18:05:14 -070031from signal import SIGINT, signal
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080032
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070033import Deploy
Udi Jonnalagaddadf49fb82020-03-17 11:05:17 -070034import utils
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080035from CfgFactory import make_cfg
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080036
37# TODO: add dvsim_cfg.hjson to retrieve this info
38version = 0.1
39
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080040# By default, all build and run artifacts go here.
41DEFAULT_SCRATCH_ROOT = os.getcwd() + "/scratch"
42
Rupert Swarbricke83b55e2020-05-12 11:44:04 +010043# The different categories that can be passed to the --list argument.
44_LIST_CATEGORIES = ["build_modes", "run_modes", "tests", "regressions"]
45
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080046
47# Function to resolve the scratch root directory among the available options:
48# If set on the command line, then use that as a preference.
49# Else, check if $SCRATCH_ROOT env variable exists and is a directory.
50# Else use the default (<cwd>/scratch)
51# Try to create the directory if it does not already exist.
52def resolve_scratch_root(arg_scratch_root):
53 scratch_root = os.environ.get('SCRATCH_ROOT')
Rupert Swarbrick06383682020-03-24 15:56:41 +000054 if not arg_scratch_root:
55 if scratch_root is None:
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080056 arg_scratch_root = DEFAULT_SCRATCH_ROOT
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080057 else:
58 # Scratch space could be mounted in a filesystem (such as NFS) on a network drive.
59 # If the network is down, it could cause the access access check to hang. So run a
60 # simple ls command with a timeout to prevent the hang.
61 (out,
62 status) = utils.run_cmd_with_timeout(cmd="ls -d " + scratch_root,
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080063 timeout=1,
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080064 exit_on_failure=0)
65 if status == 0 and out != "":
66 arg_scratch_root = scratch_root
67 else:
Srikrishna Iyer981c36b2020-12-12 12:20:35 -080068 arg_scratch_root = DEFAULT_SCRATCH_ROOT
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080069 log.warning(
Rupert Swarbrick06383682020-03-24 15:56:41 +000070 "Env variable $SCRATCH_ROOT=\"{}\" is not accessible.\n"
71 "Using \"{}\" instead.".format(scratch_root,
72 arg_scratch_root))
Srikrishna Iyer544da8d2020-01-14 23:51:41 -080073 else:
74 arg_scratch_root = os.path.realpath(arg_scratch_root)
75
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080076 try:
77 os.system("mkdir -p " + arg_scratch_root)
Rupert Swarbrick06383682020-03-24 15:56:41 +000078 except OSError:
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080079 log.fatal(
80 "Invalid --scratch-root=\"%s\" switch - failed to create directory!",
81 arg_scratch_root)
82 sys.exit(1)
83 return (arg_scratch_root)
84
85
Rupert Swarbrick536ab202020-06-09 16:37:12 +010086def read_max_parallel(arg):
87 '''Take value for --max-parallel as an integer'''
88 try:
89 int_val = int(arg)
90 if int_val <= 0:
91 raise ValueError('bad value')
92 return int_val
93
94 except ValueError:
Weicai Yang813d6892020-10-19 16:20:31 -070095 raise argparse.ArgumentTypeError(
96 'Bad argument for --max-parallel '
97 '({!r}): must be a positive integer.'.format(arg))
Rupert Swarbrick536ab202020-06-09 16:37:12 +010098
99
100def resolve_max_parallel(arg):
101 '''Pick a value of max_parallel, defaulting to 16 or $DVSIM_MAX_PARALLEL'''
102 if arg is not None:
103 assert arg > 0
104 return arg
105
106 from_env = os.environ.get('DVSIM_MAX_PARALLEL')
107 if from_env is not None:
108 try:
109 return read_max_parallel(from_env)
110 except argparse.ArgumentTypeError:
111 log.warning('DVSIM_MAX_PARALLEL environment variable has value '
112 '{!r}, which is not a positive integer. Using default '
Weicai Yang813d6892020-10-19 16:20:31 -0700113 'value (16).'.format(from_env))
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100114
115 return 16
116
117
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100118def resolve_branch(branch):
119 '''Choose a branch name for output files
120
121 If the --branch argument was passed on the command line, the branch
122 argument is the branch name to use. Otherwise it is None and we use git to
123 find the name of the current branch in the working directory.
124
125 '''
126
127 if branch is not None:
128 return branch
129
130 result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"],
131 stdout=subprocess.PIPE)
132 branch = result.stdout.decode("utf-8").strip()
133 if not branch:
134 log.warning("Failed to find current git branch. "
135 "Setting it to \"default\"")
136 branch = "default"
137
138 return branch
139
140
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800141# Get the project root directory path - this is used to construct the full paths
142def get_proj_root():
Rupert Swarbrick06383682020-03-24 15:56:41 +0000143 cmd = ["git", "rev-parse", "--show-toplevel"]
144 result = subprocess.run(cmd,
Rupert Swarbrick1dd327bd2020-03-24 15:14:00 +0000145 stdout=subprocess.PIPE,
146 stderr=subprocess.PIPE)
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700147 proj_root = result.stdout.decode("utf-8").strip()
Rupert Swarbrick06383682020-03-24 15:56:41 +0000148 if not proj_root:
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700149 log.error(
Rupert Swarbrick06383682020-03-24 15:56:41 +0000150 "Attempted to find the root of this GitHub repository by running:\n"
151 "{}\n"
152 "But this command has failed:\n"
153 "{}".format(' '.join(cmd), result.stderr.decode("utf-8")))
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700154 sys.exit(1)
155 return (proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800156
157
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800158def resolve_proj_root(args):
159 '''Update proj_root based on how DVSim is invoked.
160
161 If --remote env var is set, a location in the scratch area is chosen as the
162 new proj_root. The entire repo is copied over to this location. Else, the
163 proj_root is discovered using get_proj_root() method, unless the user
164 overrides it on the command line.
165
Cindy Chen13a5dde2020-12-21 09:31:56 -0800166 This function returns the updated proj_root src and destination path. If
167 --remote env var is not set, the destination path is identical to the src path.
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800168 '''
Cindy Chen13a5dde2020-12-21 09:31:56 -0800169 proj_root_src = args.proj_root or get_proj_root()
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800170
171 # Check if jobs are dispatched to external compute machines. If yes,
172 # then the repo needs to be copied over to the scratch area
173 # accessible to those machines.
Cindy Chenec6449e2020-12-17 11:55:38 -0800174 # If --purge arg is set, then purge the repo_top that was copied before.
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800175 if args.remote:
Cindy Chen13a5dde2020-12-21 09:31:56 -0800176 proj_root_dest = os.path.join(args.scratch_root, args.branch,
177 "repo_top")
Srikrishna Iyerab4327e2020-12-29 12:49:07 -0800178 if args.purge:
179 shutil.rmtree(proj_root_dest, ignore_errors=True)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800180 copy_repo(proj_root_src, proj_root_dest, args.dry_run)
181 else:
182 proj_root_dest = proj_root_src
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800183
Cindy Chen13a5dde2020-12-21 09:31:56 -0800184 return proj_root_src, proj_root_dest
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800185
186
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700187def sigint_handler(signal_received, frame):
188 # Kill processes and background jobs.
189 log.debug('SIGINT or CTRL-C detected. Exiting gracefully')
190 cfg.kill()
191 log.info('Exit due to SIGINT or CTRL-C ')
192 exit(1)
193
194
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800195def copy_repo(src, dest, dry_run):
196 '''Copy over the repo to a new location.
197
198 The repo is copied over from src to dest area. It tentatively uses the
199 rsync utility which provides the ability to specify a file containing some
200 exclude patterns to skip certain things from being copied over. With GitHub
201 repos, an existing `.gitignore` serves this purpose pretty well.
202 '''
Srikrishna Iyerab4327e2020-12-29 12:49:07 -0800203 rsync_cmd = "rsync --recursive --links --checksum --update --inplace "
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800204
205 # Supply `.gitignore` from the src area to skip temp files.
206 ignore_patterns_file = os.path.join(src, ".gitignore")
207 if os.path.exists(ignore_patterns_file):
208 # TODO: hack - include hw/foundry since it is excluded in .gitignore.
209 rsync_cmd += "--include=hw/foundry "
210 rsync_cmd += "--exclude-from={} ".format(ignore_patterns_file)
Eunchan Kim87e8f852021-01-05 09:03:01 -0800211 rsync_cmd += "--exclude={} ".format(shlex.quote('.*'))
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800212
213 rsync_cmd += src + "/. " + dest
214
215 cmd = ["flock", "--timeout", "600", dest, "--command", rsync_cmd]
216
217 log.info("[copy_repo] [dest]: %s", dest)
218 log.log(utils.VERBOSE, "[copy_repo] [cmd]: \n%s", ' '.join(cmd))
219 if not dry_run:
220 # Make sure the dest exists first.
221 os.makedirs(dest, exist_ok=True)
222 try:
Eunchan Kim87e8f852021-01-05 09:03:01 -0800223 subprocess.run(cmd,
224 check=True,
225 stdout=subprocess.PIPE,
226 stderr=subprocess.PIPE)
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800227 except subprocess.CalledProcessError as e:
228 log.error("Failed to copy over %s to %s: %s", src, dest,
229 e.stderr.decode("utf-8").strip())
230 log.info("Done.")
231
232
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100233def wrapped_docstring():
234 '''Return a text-wrapped version of the module docstring'''
235 paras = []
236 para = []
237 for line in __doc__.strip().split('\n'):
238 line = line.strip()
239 if not line:
240 if para:
241 paras.append('\n'.join(para))
242 para = []
243 else:
244 para.append(line)
245 if para:
246 paras.append('\n'.join(para))
247
248 return '\n\n'.join(textwrap.fill(p) for p in paras)
249
250
251def parse_args():
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800252 parser = argparse.ArgumentParser(
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100253 description=wrapped_docstring(),
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800254 formatter_class=argparse.RawDescriptionHelpFormatter)
255
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800256 parser.add_argument("cfg",
257 metavar="<cfg-hjson-file>",
258 help="""Configuration hjson file.""")
259
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800260 parser.add_argument("--version",
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800261 action='store_true',
262 help="Print version and exit")
263
Weicai Yang813d6892020-10-19 16:20:31 -0700264 parser.add_argument(
265 "--tool",
266 "-t",
267 help=("Explicitly set the tool to use. This is "
268 "optional for running simulations (where it can "
269 "be set in an .hjson file), but is required for "
270 "other flows. Possible tools include: vcs, "
271 "xcelium, ascentlint, veriblelint, verilator, dc."))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800272
Weicai Yang813d6892020-10-19 16:20:31 -0700273 parser.add_argument("--list",
274 "-l",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100275 nargs="*",
276 metavar='CAT',
277 choices=_LIST_CATEGORIES,
278 help=('Parse the the given .hjson config file, list '
279 'the things that can be run, then exit. The '
280 'list can be filtered with a space-separated '
Weicai Yang813d6892020-10-19 16:20:31 -0700281 'of categories from: {}.'.format(
282 ', '.join(_LIST_CATEGORIES))))
Srikrishna Iyer64009052020-01-13 11:27:39 -0800283
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100284 whatg = parser.add_argument_group('Choosing what to run')
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800285
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100286 whatg.add_argument("-i",
287 "--items",
288 nargs="*",
Cindy Chene513e362020-11-11 10:28:54 -0800289 default=["smoke"],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100290 help=('Specify the regressions or tests to run. '
Cindy Chene513e362020-11-11 10:28:54 -0800291 'Defaults to "smoke", but can be a '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100292 'space separated list of test or regression '
293 'names.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800294
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100295 whatg.add_argument("--select-cfgs",
296 nargs="*",
297 metavar="CFG",
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700298 help=('The .hjson file is a primary config. Only run '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100299 'the given configs from it. If this argument is '
300 'not used, dvsim will process all configs listed '
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700301 'in a primary config.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800302
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100303 disg = parser.add_argument_group('Dispatch options')
304
305 disg.add_argument("--job-prefix",
306 default="",
307 metavar="PFX",
308 help=('Prepend this string when running each tool '
309 'command.'))
310
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800311 disg.add_argument("--remote",
312 action='store_true',
313 help=('Trigger copying of the repo to scratch area.'))
314
Weicai Yang813d6892020-10-19 16:20:31 -0700315 disg.add_argument("--max-parallel",
316 "-mp",
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100317 type=read_max_parallel,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100318 metavar="N",
319 help=('Run only up to N builds/tests at a time. '
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100320 'Default value 16, unless the DVSIM_MAX_PARALLEL '
321 'environment variable is set, in which case that '
322 'is used.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100323
324 pathg = parser.add_argument_group('File management')
325
Weicai Yang813d6892020-10-19 16:20:31 -0700326 pathg.add_argument("--scratch-root",
327 "-sr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100328 metavar="PATH",
329 help=('Destination for build / run directories. If not '
330 'specified, uses the path in the SCRATCH_ROOT '
331 'environment variable, if set, or ./scratch '
332 'otherwise.'))
333
Weicai Yang813d6892020-10-19 16:20:31 -0700334 pathg.add_argument("--proj-root",
335 "-pr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100336 metavar="PATH",
337 help=('The root directory of the project. If not '
338 'specified, dvsim will search for a git '
339 'repository containing the current directory.'))
340
Weicai Yang813d6892020-10-19 16:20:31 -0700341 pathg.add_argument("--branch",
342 "-br",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100343 metavar='B',
344 help=('By default, dvsim creates files below '
345 '{scratch-root}/{dut}.{flow}.{tool}/{branch}. '
346 'If --branch is not specified, dvsim assumes the '
347 'current directory is a git repository and uses '
348 'the name of the current branch.'))
349
Weicai Yang813d6892020-10-19 16:20:31 -0700350 pathg.add_argument("--max-odirs",
351 "-mo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100352 type=int,
353 default=5,
354 metavar="N",
355 help=('When tests are run, older runs are backed '
356 'up. Discard all but the N most recent (defaults '
357 'to 5).'))
358
359 pathg.add_argument("--purge",
360 action='store_true',
361 help="Clean the scratch directory before running.")
362
363 buildg = parser.add_argument_group('Options for building')
364
365 buildg.add_argument("--build-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800366 "-bu",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100367 action='store_true',
368 help=('Stop after building executables for the given '
369 'items.'))
370
Weicai Yang813d6892020-10-19 16:20:31 -0700371 buildg.add_argument("--build-unique",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100372 action='store_true',
373 help=('Append a timestamp to the directory in which '
374 'files are built. This is suitable for the case '
375 'when another test is already running and you '
376 'want to run something else from a different '
377 'terminal without affecting it.'))
378
Weicai Yang813d6892020-10-19 16:20:31 -0700379 buildg.add_argument("--build-opts",
380 "-bo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100381 nargs="+",
382 default=[],
383 metavar="OPT",
384 help=('Additional options passed on the command line '
385 'each time a build tool is run.'))
386
Weicai Yang813d6892020-10-19 16:20:31 -0700387 buildg.add_argument("--build-modes",
388 "-bm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100389 nargs="+",
390 default=[],
391 metavar="MODE",
392 help=('The options for each build_mode in this list '
393 'are applied to all build and run targets.'))
394
395 rung = parser.add_argument_group('Options for running')
396
397 rung.add_argument("--run-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800398 "-ru",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100399 action='store_true',
400 help=('Skip the build step (assume that simulation '
401 'executables have already been built).'))
402
Weicai Yang813d6892020-10-19 16:20:31 -0700403 rung.add_argument("--run-opts",
404 "-ro",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100405 nargs="+",
406 default=[],
407 metavar="OPT",
408 help=('Additional options passed on the command line '
409 'each time a test is run.'))
410
Weicai Yang813d6892020-10-19 16:20:31 -0700411 rung.add_argument("--run-modes",
412 "-rm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100413 nargs="+",
414 default=[],
415 metavar="MODE",
416 help=('The options for each run_mode in this list are '
417 'applied to each simulation run.'))
418
Weicai Yang813d6892020-10-19 16:20:31 -0700419 rung.add_argument("--profile",
420 "-p",
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800421 nargs="?",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100422 choices=['time', 'mem'],
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800423 const="time",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100424 metavar="P",
425 help=('Turn on simulation profiling (where P is time '
426 'or mem).'))
427
428 rung.add_argument("--xprop-off",
429 action='store_true',
430 help="Turn off X-propagation in simulation.")
431
432 rung.add_argument("--no-rerun",
433 action='store_true',
434 help=("Disable the default behaviour, where failing "
435 "tests are automatically rerun with waves "
436 "enabled."))
437
Weicai Yang813d6892020-10-19 16:20:31 -0700438 rung.add_argument("--verbosity",
439 "-v",
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800440 choices=['n', 'l', 'm', 'h', 'f', 'd'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100441 metavar='V',
Rupert Swarbrickdcec9942020-09-04 08:15:07 +0100442 help=('Set tool/simulation verbosity to none (n), low '
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800443 '(l), medium (m), high (h), full (f) or debug (d).'
444 ' The default value is set in config files.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100445
446 seedg = parser.add_argument_group('Test seeds')
447
Weicai Yang813d6892020-10-19 16:20:31 -0700448 seedg.add_argument("--seeds",
449 "-s",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100450 nargs="+",
451 default=[],
452 metavar="S",
453 help=('A list of seeds for tests. Note that these '
454 'specific seeds are applied to items being run '
455 'in the order they are passed.'))
456
457 seedg.add_argument("--fixed-seed",
458 type=int,
459 metavar='S',
460 help=('Run all items with the seed S. This implies '
461 '--reseed 1.'))
462
Weicai Yang813d6892020-10-19 16:20:31 -0700463 seedg.add_argument("--reseed",
464 "-r",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100465 type=int,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100466 metavar="N",
467 help=('Override any reseed value in the test '
468 'configuration and run each test N times, with '
469 'a new seed each time.'))
470
Weicai Yang813d6892020-10-19 16:20:31 -0700471 seedg.add_argument("--reseed-multiplier",
472 "-rx",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100473 type=int,
474 default=1,
475 metavar="N",
476 help=('Scale each reseed value in the test '
477 'configuration by N. This allows e.g. running '
478 'the tests 10 times as much as normal while '
479 'maintaining the ratio of numbers of runs '
480 'between different tests.'))
481
482 waveg = parser.add_argument_group('Dumping waves')
483
Weicai Yang813d6892020-10-19 16:20:31 -0700484 waveg.add_argument(
485 "--waves",
486 "-w",
487 nargs="?",
488 choices=["default", "fsdb", "shm", "vpd", "vcd", "evcd", "fst"],
489 const="default",
490 help=("Enable dumping of waves. It takes an optional "
491 "argument to pick the desired wave format. If "
492 "the optional argument is not supplied, it picks "
493 "whatever is the default for the chosen tool. "
494 "By default, dumping waves is not enabled."))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100495
Weicai Yang813d6892020-10-19 16:20:31 -0700496 waveg.add_argument("--max-waves",
497 "-mw",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100498 type=int,
499 default=5,
500 metavar="N",
501 help=('Only dump waves for the first N tests run. This '
502 'includes both tests scheduled for run and those '
503 'that are automatically rerun.'))
504
505 covg = parser.add_argument_group('Generating simulation coverage')
506
Weicai Yang813d6892020-10-19 16:20:31 -0700507 covg.add_argument("--cov",
508 "-c",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100509 action='store_true',
510 help="Enable collection of coverage data.")
511
512 covg.add_argument("--cov-merge-previous",
513 action='store_true',
514 help=('Only applicable with --cov. Merge any previous '
515 'coverage database directory with the new '
516 'coverage database.'))
517
Weicai Yang080632d2020-10-16 17:52:14 -0700518 covg.add_argument("--cov-unr",
519 action='store_true',
520 help=('Run coverage UNR analysis and generate report. '
521 'This only supports VCS now.'))
522
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100523 covg.add_argument("--cov-analyze",
524 action='store_true',
525 help=('Rather than building or running any tests, '
526 'analyze the coverage from the last run.'))
527
528 pubg = parser.add_argument_group('Generating and publishing results')
529
530 pubg.add_argument("--map-full-testplan",
531 action='store_true',
532 help=("Show complete testplan annotated results "
533 "at the end."))
534
535 pubg.add_argument("--publish",
536 action='store_true',
537 help="Publish results to reports.opentitan.org.")
538
539 dvg = parser.add_argument_group('Controlling DVSim itself')
540
Weicai Yang813d6892020-10-19 16:20:31 -0700541 dvg.add_argument("--print-interval",
542 "-pi",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100543 type=int,
544 default=10,
545 metavar="N",
546 help="Print status every N seconds.")
547
548 dvg.add_argument("--verbose",
549 nargs="?",
550 choices=['default', 'debug'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100551 const="default",
552 metavar="D",
553 help=('With no argument, print verbose dvsim tool '
554 'messages. With --verbose=debug, the volume of '
555 'messages is even higher.'))
556
Weicai Yang813d6892020-10-19 16:20:31 -0700557 dvg.add_argument("--dry-run",
558 "-n",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100559 action='store_true',
560 help=("Print dvsim tool messages but don't actually "
561 "run any command"))
562
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100563 args = parser.parse_args()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800564
565 if args.version:
566 print(version)
567 sys.exit()
568
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100569 # We want the --list argument to default to "all categories", but allow
570 # filtering. If args.list is None, then --list wasn't supplied. If it is
571 # [], then --list was supplied with no further arguments and we want to
572 # list all categories.
573 if args.list == []:
574 args.list = _LIST_CATEGORIES
575
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100576 # Get max_parallel from environment if it wasn't specified on the command
577 # line.
578 args.max_parallel = resolve_max_parallel(args.max_parallel)
579 assert args.max_parallel > 0
580
581 return args
582
583
584def main():
585 args = parse_args()
586
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800587 # Add log level 'VERBOSE' between INFO and DEBUG
588 log.addLevelName(utils.VERBOSE, 'VERBOSE')
589
590 log_format = '%(levelname)s: [%(module)s] %(message)s'
591 log_level = log.INFO
Rupert Swarbrick06383682020-03-24 15:56:41 +0000592 if args.verbose == "default":
593 log_level = utils.VERBOSE
594 elif args.verbose == "debug":
595 log_level = log.DEBUG
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800596 log.basicConfig(format=log_format, level=log_level)
597
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800598 if not os.path.exists(args.cfg):
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800599 log.fatal("Path to config file %s appears to be invalid.", args.cfg)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800600 sys.exit(1)
601
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800602 # If publishing results, then force full testplan mapping of results.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000603 if args.publish:
604 args.map_full_testplan = True
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800605
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800606 args.scratch_root = resolve_scratch_root(args.scratch_root)
607 args.branch = resolve_branch(args.branch)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800608 proj_root_src, proj_root = resolve_proj_root(args)
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800609 log.info("[proj_root]: %s", proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800610
Cindy Chen13a5dde2020-12-21 09:31:56 -0800611 args.cfg = os.path.abspath(args.cfg)
612 if args.remote:
613 cfg_path = args.cfg.replace(proj_root_src + "/", "")
614 args.cfg = os.path.join(proj_root, cfg_path)
615
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800616 # Add timestamp to args that all downstream objects can use.
617 # Static variables - indicate timestamp.
Eunchan Kim46125cd2020-04-09 09:26:52 -0700618 ts_format_long = "%A %B %d %Y %I:%M:%S%p UTC"
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800619 ts_format = "%a.%m.%d.%y__%I.%M.%S%p"
Eunchan Kim46125cd2020-04-09 09:26:52 -0700620 curr_ts = datetime.datetime.utcnow()
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800621 timestamp_long = curr_ts.strftime(ts_format_long)
622 timestamp = curr_ts.strftime(ts_format)
623 setattr(args, "ts_format_long", ts_format_long)
624 setattr(args, "ts_format", ts_format)
625 setattr(args, "timestamp_long", timestamp_long)
626 setattr(args, "timestamp", timestamp)
627
628 # Register the seeds from command line with RunTest class.
629 Deploy.RunTest.seeds = args.seeds
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700630 # If we are fixing a seed value, no point in tests having multiple reseeds.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000631 if args.fixed_seed:
632 args.reseed = 1
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700633 Deploy.RunTest.fixed_seed = args.fixed_seed
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800634
635 # Register the common deploy settings.
636 Deploy.Deploy.print_interval = args.print_interval
637 Deploy.Deploy.max_parallel = args.max_parallel
638 Deploy.Deploy.max_odirs = args.max_odirs
639
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800640 # Build infrastructure from hjson file and create the list of items to
641 # be deployed.
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700642 global cfg
Rupert Swarbricka23dfec2020-09-07 10:01:28 +0100643 cfg = make_cfg(args.cfg, args, proj_root)
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100644
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700645 # Handle Ctrl-C exit.
646 signal(SIGINT, sigint_handler)
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700647
Srikrishna Iyer64009052020-01-13 11:27:39 -0800648 # List items available for run if --list switch is passed, and exit.
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100649 if args.list is not None:
Srikrishna Iyer64009052020-01-13 11:27:39 -0800650 cfg.print_list()
651 sys.exit(0)
652
Weicai Yang080632d2020-10-16 17:52:14 -0700653 # Purge the scratch path if --purge option is set.
654 if args.purge:
655 cfg.purge()
656
657 # If --cov-unr is passed, run UNR to generate report for unreachable
658 # exclusion file.
659 if args.cov_unr:
660 cfg.cov_unr()
661 cfg.deploy_objects()
662 sys.exit(0)
663
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800664 # In simulation mode: if --cov-analyze switch is passed, then run the GUI
665 # tool.
666 if args.cov_analyze:
667 cfg.cov_analyze()
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700668 cfg.deploy_objects()
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800669 sys.exit(0)
670
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800671 # Deploy the builds and runs
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800672 if args.items != []:
673 # Create deploy objects.
674 cfg.create_deploy_objects()
675 cfg.deploy_objects()
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800676
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800677 # Generate results.
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800678 cfg.gen_results()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800679
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800680 # Publish results
Rupert Swarbrick06383682020-03-24 15:56:41 +0000681 if args.publish:
682 cfg.publish_results()
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800683
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800684 else:
685 log.info("No items specified to be run.")
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800686
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800687 # Exit with non-zero status if there were errors or failures.
688 if cfg.has_errors():
689 log.error("Errors were encountered in this run.")
690 sys.exit(1)
691
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800692
693if __name__ == '__main__':
694 main()