blob: add4d2145fe64b9743263d66cd898a07077e5c95 [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
Eunchan Kim87e8f852021-01-05 09:03:01 -080026import shlex
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080027import subprocess
28import sys
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +010029import textwrap
Srikrishna Iyer2d151192021-02-10 16:56:16 -080030from pathlib import Path
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080031
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -080032import Launcher
33import LauncherFactory
Srikrishna Iyer2d151192021-02-10 16:56:16 -080034from CfgFactory import make_cfg
Srikrishna Iyer65783742021-02-10 21:42:12 -080035from Deploy import RunTest
Rupert Swarbrickc0e753d2020-06-04 13:27:58 +010036from Scheduler import Scheduler
37from Timer import Timer
Srikrishna Iyer2b944ad2021-03-01 13:05:40 -080038from utils import (TS_FORMAT, TS_FORMAT_LONG, VERBOSE, rm_path,
39 run_cmd_with_timeout)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080040
41# TODO: add dvsim_cfg.hjson to retrieve this info
42version = 0.1
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.
Srikrishna Iyerf479b9e2021-04-08 16:03:11 -070051# Else use the default (<proj_root>/scratch)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080052# Try to create the directory if it does not already exist.
Srikrishna Iyerf479b9e2021-04-08 16:03:11 -070053def resolve_scratch_root(arg_scratch_root, proj_root):
54 default_scratch_root = proj_root + "/scratch"
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080055 scratch_root = os.environ.get('SCRATCH_ROOT')
Rupert Swarbrick06383682020-03-24 15:56:41 +000056 if not arg_scratch_root:
57 if scratch_root is None:
Srikrishna Iyerf479b9e2021-04-08 16:03:11 -070058 arg_scratch_root = default_scratch_root
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080059 else:
60 # Scratch space could be mounted in a filesystem (such as NFS) on a network drive.
61 # If the network is down, it could cause the access access check to hang. So run a
62 # simple ls command with a timeout to prevent the hang.
Srikrishna Iyer2d151192021-02-10 16:56:16 -080063 (out, status) = run_cmd_with_timeout(cmd="ls -d " + scratch_root,
64 timeout=1,
65 exit_on_failure=0)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080066 if status == 0 and out != "":
67 arg_scratch_root = scratch_root
68 else:
Srikrishna Iyerf479b9e2021-04-08 16:03:11 -070069 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:
Srikrishna Iyer2d151192021-02-10 16:56:16 -080078 os.makedirs(arg_scratch_root, exist_ok=True)
79 except PermissionError as e:
80 log.fatal("Failed to create scratch root {}:\n{}.".format(
81 arg_scratch_root, e))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080082 sys.exit(1)
Srikrishna Iyer2d151192021-02-10 16:56:16 -080083
84 if not os.access(arg_scratch_root, os.W_OK):
85 log.fatal("Scratch root {} is not writable!".format(arg_scratch_root))
86 sys.exit(1)
87
88 return arg_scratch_root
Srikrishna Iyer09a81e92019-12-30 10:47:57 -080089
90
Rupert Swarbrick536ab202020-06-09 16:37:12 +010091def read_max_parallel(arg):
92 '''Take value for --max-parallel as an integer'''
93 try:
94 int_val = int(arg)
95 if int_val <= 0:
96 raise ValueError('bad value')
97 return int_val
98
99 except ValueError:
Weicai Yang813d6892020-10-19 16:20:31 -0700100 raise argparse.ArgumentTypeError(
101 'Bad argument for --max-parallel '
102 '({!r}): must be a positive integer.'.format(arg))
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100103
104
105def resolve_max_parallel(arg):
106 '''Pick a value of max_parallel, defaulting to 16 or $DVSIM_MAX_PARALLEL'''
107 if arg is not None:
108 assert arg > 0
109 return arg
110
111 from_env = os.environ.get('DVSIM_MAX_PARALLEL')
112 if from_env is not None:
113 try:
114 return read_max_parallel(from_env)
115 except argparse.ArgumentTypeError:
116 log.warning('DVSIM_MAX_PARALLEL environment variable has value '
117 '{!r}, which is not a positive integer. Using default '
Weicai Yang813d6892020-10-19 16:20:31 -0700118 'value (16).'.format(from_env))
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100119
120 return 16
121
122
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100123def resolve_branch(branch):
124 '''Choose a branch name for output files
125
126 If the --branch argument was passed on the command line, the branch
127 argument is the branch name to use. Otherwise it is None and we use git to
128 find the name of the current branch in the working directory.
129
130 '''
131
132 if branch is not None:
133 return branch
134
135 result = subprocess.run(["git", "rev-parse", "--abbrev-ref", "HEAD"],
136 stdout=subprocess.PIPE)
137 branch = result.stdout.decode("utf-8").strip()
138 if not branch:
139 log.warning("Failed to find current git branch. "
140 "Setting it to \"default\"")
141 branch = "default"
142
143 return branch
144
145
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800146# Get the project root directory path - this is used to construct the full paths
147def get_proj_root():
Rupert Swarbrick06383682020-03-24 15:56:41 +0000148 cmd = ["git", "rev-parse", "--show-toplevel"]
149 result = subprocess.run(cmd,
Rupert Swarbrick1dd327bd2020-03-24 15:14:00 +0000150 stdout=subprocess.PIPE,
151 stderr=subprocess.PIPE)
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700152 proj_root = result.stdout.decode("utf-8").strip()
Rupert Swarbrick06383682020-03-24 15:56:41 +0000153 if not proj_root:
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700154 log.error(
Rupert Swarbrick06383682020-03-24 15:56:41 +0000155 "Attempted to find the root of this GitHub repository by running:\n"
156 "{}\n"
157 "But this command has failed:\n"
158 "{}".format(' '.join(cmd), result.stderr.decode("utf-8")))
Udi Jonnalagaddaa122dda2020-03-13 16:56:45 -0700159 sys.exit(1)
160 return (proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800161
162
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800163def resolve_proj_root(args):
164 '''Update proj_root based on how DVSim is invoked.
165
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800166 If --remote switch is set, a location in the scratch area is chosen as the
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800167 new proj_root. The entire repo is copied over to this location. Else, the
168 proj_root is discovered using get_proj_root() method, unless the user
169 overrides it on the command line.
170
Cindy Chen13a5dde2020-12-21 09:31:56 -0800171 This function returns the updated proj_root src and destination path. If
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800172 --remote switch is not set, the destination path is identical to the src
173 path. Likewise, if --dry-run is set.
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800174 '''
Cindy Chen13a5dde2020-12-21 09:31:56 -0800175 proj_root_src = args.proj_root or get_proj_root()
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800176
177 # Check if jobs are dispatched to external compute machines. If yes,
178 # then the repo needs to be copied over to the scratch area
179 # accessible to those machines.
Cindy Chenec6449e2020-12-17 11:55:38 -0800180 # If --purge arg is set, then purge the repo_top that was copied before.
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800181 if args.remote and not args.dry_run:
Cindy Chen13a5dde2020-12-21 09:31:56 -0800182 proj_root_dest = os.path.join(args.scratch_root, args.branch,
183 "repo_top")
Srikrishna Iyerab4327e2020-12-29 12:49:07 -0800184 if args.purge:
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800185 rm_path(proj_root_dest)
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800186 copy_repo(proj_root_src, proj_root_dest)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800187 else:
188 proj_root_dest = proj_root_src
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800189
Cindy Chen13a5dde2020-12-21 09:31:56 -0800190 return proj_root_src, proj_root_dest
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800191
192
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800193def copy_repo(src, dest):
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800194 '''Copy over the repo to a new location.
195
196 The repo is copied over from src to dest area. It tentatively uses the
197 rsync utility which provides the ability to specify a file containing some
198 exclude patterns to skip certain things from being copied over. With GitHub
199 repos, an existing `.gitignore` serves this purpose pretty well.
200 '''
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800201 rsync_cmd = [
202 "rsync", "--recursive", "--links", "--checksum", "--update",
203 "--inplace", "--no-group"
204 ]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800205
206 # Supply `.gitignore` from the src area to skip temp files.
207 ignore_patterns_file = os.path.join(src, ".gitignore")
208 if os.path.exists(ignore_patterns_file):
209 # TODO: hack - include hw/foundry since it is excluded in .gitignore.
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800210 rsync_cmd += [
211 "--include=hw/foundry",
212 "--exclude-from={}".format(ignore_patterns_file), "--exclude=.*"
213 ]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800214
Eunchan Kim425a0b82021-01-06 09:25:10 -0800215 rsync_cmd += [src + "/.", dest]
216 rsync_str = ' '.join([shlex.quote(w) for w in rsync_cmd])
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800217
Eunchan Kim425a0b82021-01-06 09:25:10 -0800218 cmd = ["flock", "--timeout", "600", dest, "--command", rsync_str]
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800219
220 log.info("[copy_repo] [dest]: %s", dest)
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800221 log.log(VERBOSE, "[copy_repo] [cmd]: \n%s", ' '.join(cmd))
Srikrishna Iyere3af4cf2021-03-09 17:22:01 -0800222
223 # Make sure the dest exists first.
224 os.makedirs(dest, exist_ok=True)
225 try:
226 subprocess.run(cmd,
227 check=True,
228 stdout=subprocess.PIPE,
229 stderr=subprocess.PIPE)
230 except subprocess.CalledProcessError as e:
231 log.error("Failed to copy over %s to %s: %s", src, dest,
232 e.stderr.decode("utf-8").strip())
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800233 log.info("Done.")
234
235
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100236def wrapped_docstring():
237 '''Return a text-wrapped version of the module docstring'''
238 paras = []
239 para = []
240 for line in __doc__.strip().split('\n'):
241 line = line.strip()
242 if not line:
243 if para:
244 paras.append('\n'.join(para))
245 para = []
246 else:
247 para.append(line)
248 if para:
249 paras.append('\n'.join(para))
250
251 return '\n\n'.join(textwrap.fill(p) for p in paras)
252
253
254def parse_args():
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800255 parser = argparse.ArgumentParser(
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100256 description=wrapped_docstring(),
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800257 formatter_class=argparse.RawDescriptionHelpFormatter)
258
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800259 parser.add_argument("cfg",
260 metavar="<cfg-hjson-file>",
261 help="""Configuration hjson file.""")
262
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800263 parser.add_argument("--version",
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800264 action='store_true',
265 help="Print version and exit")
266
Weicai Yang813d6892020-10-19 16:20:31 -0700267 parser.add_argument(
268 "--tool",
269 "-t",
270 help=("Explicitly set the tool to use. This is "
271 "optional for running simulations (where it can "
272 "be set in an .hjson file), but is required for "
273 "other flows. Possible tools include: vcs, "
274 "xcelium, ascentlint, veriblelint, verilator, dc."))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800275
Weicai Yang813d6892020-10-19 16:20:31 -0700276 parser.add_argument("--list",
277 "-l",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100278 nargs="*",
279 metavar='CAT',
280 choices=_LIST_CATEGORIES,
281 help=('Parse the the given .hjson config file, list '
282 'the things that can be run, then exit. The '
283 'list can be filtered with a space-separated '
Weicai Yang813d6892020-10-19 16:20:31 -0700284 'of categories from: {}.'.format(
285 ', '.join(_LIST_CATEGORIES))))
Srikrishna Iyer64009052020-01-13 11:27:39 -0800286
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100287 whatg = parser.add_argument_group('Choosing what to run')
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800288
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100289 whatg.add_argument("-i",
290 "--items",
291 nargs="*",
Cindy Chene513e362020-11-11 10:28:54 -0800292 default=["smoke"],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100293 help=('Specify the regressions or tests to run. '
Cindy Chene513e362020-11-11 10:28:54 -0800294 'Defaults to "smoke", but can be a '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100295 'space separated list of test or regression '
296 'names.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800297
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100298 whatg.add_argument("--select-cfgs",
299 nargs="*",
300 metavar="CFG",
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700301 help=('The .hjson file is a primary config. Only run '
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100302 'the given configs from it. If this argument is '
303 'not used, dvsim will process all configs listed '
Scott Johnsonfe79c4b2020-07-08 10:31:08 -0700304 'in a primary config.'))
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800305
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100306 disg = parser.add_argument_group('Dispatch options')
307
308 disg.add_argument("--job-prefix",
309 default="",
310 metavar="PFX",
311 help=('Prepend this string when running each tool '
312 'command.'))
313
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -0800314 disg.add_argument("--local",
315 action='store_true',
316 help=('Force jobs to be dispatched locally onto user\'s '
317 'machine.'))
318
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800319 disg.add_argument("--remote",
320 action='store_true',
321 help=('Trigger copying of the repo to scratch area.'))
322
Weicai Yang813d6892020-10-19 16:20:31 -0700323 disg.add_argument("--max-parallel",
324 "-mp",
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100325 type=read_max_parallel,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100326 metavar="N",
327 help=('Run only up to N builds/tests at a time. '
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100328 'Default value 16, unless the DVSIM_MAX_PARALLEL '
329 'environment variable is set, in which case that '
330 'is used.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100331
332 pathg = parser.add_argument_group('File management')
333
Weicai Yang813d6892020-10-19 16:20:31 -0700334 pathg.add_argument("--scratch-root",
335 "-sr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100336 metavar="PATH",
337 help=('Destination for build / run directories. If not '
338 'specified, uses the path in the SCRATCH_ROOT '
339 'environment variable, if set, or ./scratch '
340 'otherwise.'))
341
Weicai Yang813d6892020-10-19 16:20:31 -0700342 pathg.add_argument("--proj-root",
343 "-pr",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100344 metavar="PATH",
345 help=('The root directory of the project. If not '
346 'specified, dvsim will search for a git '
347 'repository containing the current directory.'))
348
Weicai Yang813d6892020-10-19 16:20:31 -0700349 pathg.add_argument("--branch",
350 "-br",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100351 metavar='B',
352 help=('By default, dvsim creates files below '
353 '{scratch-root}/{dut}.{flow}.{tool}/{branch}. '
354 'If --branch is not specified, dvsim assumes the '
355 'current directory is a git repository and uses '
356 'the name of the current branch.'))
357
Weicai Yang813d6892020-10-19 16:20:31 -0700358 pathg.add_argument("--max-odirs",
359 "-mo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100360 type=int,
361 default=5,
362 metavar="N",
363 help=('When tests are run, older runs are backed '
364 'up. Discard all but the N most recent (defaults '
365 'to 5).'))
366
367 pathg.add_argument("--purge",
368 action='store_true',
369 help="Clean the scratch directory before running.")
370
371 buildg = parser.add_argument_group('Options for building')
372
373 buildg.add_argument("--build-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800374 "-bu",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100375 action='store_true',
376 help=('Stop after building executables for the given '
377 'items.'))
378
Weicai Yang813d6892020-10-19 16:20:31 -0700379 buildg.add_argument("--build-unique",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100380 action='store_true',
381 help=('Append a timestamp to the directory in which '
382 'files are built. This is suitable for the case '
383 'when another test is already running and you '
384 'want to run something else from a different '
385 'terminal without affecting it.'))
386
Weicai Yang813d6892020-10-19 16:20:31 -0700387 buildg.add_argument("--build-opts",
388 "-bo",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100389 nargs="+",
390 default=[],
391 metavar="OPT",
392 help=('Additional options passed on the command line '
393 'each time a build tool is run.'))
394
Weicai Yang813d6892020-10-19 16:20:31 -0700395 buildg.add_argument("--build-modes",
396 "-bm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100397 nargs="+",
398 default=[],
399 metavar="MODE",
400 help=('The options for each build_mode in this list '
401 'are applied to all build and run targets.'))
402
Srikrishna Iyerd37d10d2021-03-25 18:35:36 -0700403 disg.add_argument("--gui",
404 action='store_true',
405 help=('Run the flow in interactive mode instead of the '
406 'batch mode.'))
407
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100408 rung = parser.add_argument_group('Options for running')
409
410 rung.add_argument("--run-only",
Weicai Yang528b3c02020-12-04 16:13:16 -0800411 "-ru",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100412 action='store_true',
413 help=('Skip the build step (assume that simulation '
414 'executables have already been built).'))
415
Weicai Yang813d6892020-10-19 16:20:31 -0700416 rung.add_argument("--run-opts",
417 "-ro",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100418 nargs="+",
419 default=[],
420 metavar="OPT",
421 help=('Additional options passed on the command line '
422 'each time a test is run.'))
423
Weicai Yang813d6892020-10-19 16:20:31 -0700424 rung.add_argument("--run-modes",
425 "-rm",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100426 nargs="+",
427 default=[],
428 metavar="MODE",
429 help=('The options for each run_mode in this list are '
430 'applied to each simulation run.'))
431
Weicai Yang813d6892020-10-19 16:20:31 -0700432 rung.add_argument("--profile",
433 "-p",
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800434 nargs="?",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100435 choices=['time', 'mem'],
Srikrishna Iyerf807f9d2020-11-17 01:37:52 -0800436 const="time",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100437 metavar="P",
438 help=('Turn on simulation profiling (where P is time '
439 'or mem).'))
440
441 rung.add_argument("--xprop-off",
442 action='store_true',
443 help="Turn off X-propagation in simulation.")
444
445 rung.add_argument("--no-rerun",
446 action='store_true',
447 help=("Disable the default behaviour, where failing "
448 "tests are automatically rerun with waves "
449 "enabled."))
450
Weicai Yang813d6892020-10-19 16:20:31 -0700451 rung.add_argument("--verbosity",
452 "-v",
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800453 choices=['n', 'l', 'm', 'h', 'f', 'd'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100454 metavar='V',
Rupert Swarbrickdcec9942020-09-04 08:15:07 +0100455 help=('Set tool/simulation verbosity to none (n), low '
Srikrishna Iyera4f06642020-12-04 17:46:01 -0800456 '(l), medium (m), high (h), full (f) or debug (d).'
457 ' The default value is set in config files.'))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100458
459 seedg = parser.add_argument_group('Test seeds')
460
Weicai Yang813d6892020-10-19 16:20:31 -0700461 seedg.add_argument("--seeds",
462 "-s",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100463 nargs="+",
464 default=[],
465 metavar="S",
466 help=('A list of seeds for tests. Note that these '
467 'specific seeds are applied to items being run '
468 'in the order they are passed.'))
469
470 seedg.add_argument("--fixed-seed",
471 type=int,
472 metavar='S',
473 help=('Run all items with the seed S. This implies '
474 '--reseed 1.'))
475
Weicai Yang813d6892020-10-19 16:20:31 -0700476 seedg.add_argument("--reseed",
477 "-r",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100478 type=int,
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100479 metavar="N",
480 help=('Override any reseed value in the test '
481 'configuration and run each test N times, with '
482 'a new seed each time.'))
483
Weicai Yang813d6892020-10-19 16:20:31 -0700484 seedg.add_argument("--reseed-multiplier",
485 "-rx",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100486 type=int,
487 default=1,
488 metavar="N",
489 help=('Scale each reseed value in the test '
490 'configuration by N. This allows e.g. running '
491 'the tests 10 times as much as normal while '
492 'maintaining the ratio of numbers of runs '
493 'between different tests.'))
494
495 waveg = parser.add_argument_group('Dumping waves')
496
Weicai Yang813d6892020-10-19 16:20:31 -0700497 waveg.add_argument(
498 "--waves",
499 "-w",
500 nargs="?",
501 choices=["default", "fsdb", "shm", "vpd", "vcd", "evcd", "fst"],
502 const="default",
503 help=("Enable dumping of waves. It takes an optional "
504 "argument to pick the desired wave format. If "
505 "the optional argument is not supplied, it picks "
506 "whatever is the default for the chosen tool. "
507 "By default, dumping waves is not enabled."))
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100508
Weicai Yang813d6892020-10-19 16:20:31 -0700509 waveg.add_argument("--max-waves",
510 "-mw",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100511 type=int,
512 default=5,
513 metavar="N",
514 help=('Only dump waves for the first N tests run. This '
515 'includes both tests scheduled for run and those '
516 'that are automatically rerun.'))
517
518 covg = parser.add_argument_group('Generating simulation coverage')
519
Weicai Yang813d6892020-10-19 16:20:31 -0700520 covg.add_argument("--cov",
521 "-c",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100522 action='store_true',
523 help="Enable collection of coverage data.")
524
525 covg.add_argument("--cov-merge-previous",
526 action='store_true',
527 help=('Only applicable with --cov. Merge any previous '
528 'coverage database directory with the new '
529 'coverage database.'))
530
Weicai Yang080632d2020-10-16 17:52:14 -0700531 covg.add_argument("--cov-unr",
532 action='store_true',
533 help=('Run coverage UNR analysis and generate report. '
534 'This only supports VCS now.'))
535
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100536 covg.add_argument("--cov-analyze",
537 action='store_true',
538 help=('Rather than building or running any tests, '
539 'analyze the coverage from the last run.'))
540
541 pubg = parser.add_argument_group('Generating and publishing results')
542
543 pubg.add_argument("--map-full-testplan",
544 action='store_true',
545 help=("Show complete testplan annotated results "
546 "at the end."))
547
548 pubg.add_argument("--publish",
549 action='store_true',
550 help="Publish results to reports.opentitan.org.")
551
552 dvg = parser.add_argument_group('Controlling DVSim itself')
553
Weicai Yang813d6892020-10-19 16:20:31 -0700554 dvg.add_argument("--print-interval",
555 "-pi",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100556 type=int,
557 default=10,
558 metavar="N",
559 help="Print status every N seconds.")
560
561 dvg.add_argument("--verbose",
562 nargs="?",
563 choices=['default', 'debug'],
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100564 const="default",
565 metavar="D",
566 help=('With no argument, print verbose dvsim tool '
567 'messages. With --verbose=debug, the volume of '
568 'messages is even higher.'))
569
Weicai Yang813d6892020-10-19 16:20:31 -0700570 dvg.add_argument("--dry-run",
571 "-n",
Rupert Swarbrickb7aacc12020-06-02 11:04:55 +0100572 action='store_true',
573 help=("Print dvsim tool messages but don't actually "
574 "run any command"))
575
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100576 args = parser.parse_args()
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800577
578 if args.version:
579 print(version)
580 sys.exit()
581
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100582 # We want the --list argument to default to "all categories", but allow
583 # filtering. If args.list is None, then --list wasn't supplied. If it is
584 # [], then --list was supplied with no further arguments and we want to
585 # list all categories.
586 if args.list == []:
587 args.list = _LIST_CATEGORIES
588
Rupert Swarbrick536ab202020-06-09 16:37:12 +0100589 # Get max_parallel from environment if it wasn't specified on the command
590 # line.
591 args.max_parallel = resolve_max_parallel(args.max_parallel)
592 assert args.max_parallel > 0
593
594 return args
595
596
597def main():
598 args = parse_args()
599
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800600 # Add log level 'VERBOSE' between INFO and DEBUG
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800601 log.addLevelName(VERBOSE, 'VERBOSE')
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800602
603 log_format = '%(levelname)s: [%(module)s] %(message)s'
604 log_level = log.INFO
Rupert Swarbrick06383682020-03-24 15:56:41 +0000605 if args.verbose == "default":
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800606 log_level = VERBOSE
Rupert Swarbrick06383682020-03-24 15:56:41 +0000607 elif args.verbose == "debug":
608 log_level = log.DEBUG
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800609 log.basicConfig(format=log_format, level=log_level)
610
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800611 if not os.path.exists(args.cfg):
Michael Schaffner8ac6c4c2020-03-03 15:00:20 -0800612 log.fatal("Path to config file %s appears to be invalid.", args.cfg)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800613 sys.exit(1)
614
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800615 # If publishing results, then force full testplan mapping of results.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000616 if args.publish:
617 args.map_full_testplan = True
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800618
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800619 args.branch = resolve_branch(args.branch)
Cindy Chen13a5dde2020-12-21 09:31:56 -0800620 proj_root_src, proj_root = resolve_proj_root(args)
Srikrishna Iyerf479b9e2021-04-08 16:03:11 -0700621 args.scratch_root = resolve_scratch_root(args.scratch_root, proj_root)
Srikrishna Iyer981c36b2020-12-12 12:20:35 -0800622 log.info("[proj_root]: %s", proj_root)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800623
Rupert Swarbrick62b3e762021-01-29 15:21:49 +0000624 # Create an empty FUSESOC_IGNORE file in scratch_root. This ensures that
625 # any fusesoc invocation from a job won't search within scratch_root for
626 # core files.
627 (Path(args.scratch_root) / 'FUSESOC_IGNORE').touch()
628
Cindy Chen13a5dde2020-12-21 09:31:56 -0800629 args.cfg = os.path.abspath(args.cfg)
630 if args.remote:
631 cfg_path = args.cfg.replace(proj_root_src + "/", "")
632 args.cfg = os.path.join(proj_root, cfg_path)
633
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800634 # Add timestamp to args that all downstream objects can use.
Eunchan Kim46125cd2020-04-09 09:26:52 -0700635 curr_ts = datetime.datetime.utcnow()
Srikrishna Iyer2b944ad2021-03-01 13:05:40 -0800636 setattr(args, "timestamp_long", curr_ts.strftime(TS_FORMAT_LONG))
637 setattr(args, "timestamp", curr_ts.strftime(TS_FORMAT))
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800638
639 # Register the seeds from command line with RunTest class.
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800640 RunTest.seeds = args.seeds
Srikrishna Iyer96e54102020-03-12 22:46:50 -0700641 # If we are fixing a seed value, no point in tests having multiple reseeds.
Rupert Swarbrick06383682020-03-24 15:56:41 +0000642 if args.fixed_seed:
643 args.reseed = 1
Srikrishna Iyer2d151192021-02-10 16:56:16 -0800644 RunTest.fixed_seed = args.fixed_seed
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800645
646 # Register the common deploy settings.
Rupert Swarbrickc0e753d2020-06-04 13:27:58 +0100647 Timer.print_interval = args.print_interval
648 Scheduler.max_parallel = args.max_parallel
Srikrishna Iyer9d9d86f2021-03-02 00:15:51 -0800649 Launcher.Launcher.max_odirs = args.max_odirs
650 LauncherFactory.set_launcher_type(args.local)
Srikrishna Iyer544da8d2020-01-14 23:51:41 -0800651
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800652 # Build infrastructure from hjson file and create the list of items to
653 # be deployed.
Weicai Yangfc2ff3b2020-03-19 18:05:14 -0700654 global cfg
Rupert Swarbricka23dfec2020-09-07 10:01:28 +0100655 cfg = make_cfg(args.cfg, args, proj_root)
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100656
Srikrishna Iyer64009052020-01-13 11:27:39 -0800657 # List items available for run if --list switch is passed, and exit.
Rupert Swarbricke83b55e2020-05-12 11:44:04 +0100658 if args.list is not None:
Srikrishna Iyer64009052020-01-13 11:27:39 -0800659 cfg.print_list()
660 sys.exit(0)
661
Weicai Yang080632d2020-10-16 17:52:14 -0700662 # Purge the scratch path if --purge option is set.
663 if args.purge:
664 cfg.purge()
665
666 # If --cov-unr is passed, run UNR to generate report for unreachable
667 # exclusion file.
668 if args.cov_unr:
669 cfg.cov_unr()
670 cfg.deploy_objects()
671 sys.exit(0)
672
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800673 # In simulation mode: if --cov-analyze switch is passed, then run the GUI
674 # tool.
675 if args.cov_analyze:
676 cfg.cov_analyze()
Srikrishna Iyer39ffebd2020-03-30 11:53:12 -0700677 cfg.deploy_objects()
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800678 sys.exit(0)
679
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800680 # Deploy the builds and runs
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800681 if args.items != []:
682 # Create deploy objects.
683 cfg.create_deploy_objects()
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100684 results = cfg.deploy_objects()
Srikrishna Iyer7cf7cad2020-01-08 11:32:53 -0800685
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800686 # Generate results.
Rupert Swarbricka2892a52020-10-12 08:50:08 +0100687 cfg.gen_results(results)
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800688
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800689 # Publish results
Rupert Swarbrick06383682020-03-24 15:56:41 +0000690 if args.publish:
691 cfg.publish_results()
Weicai Yangfd2c22e2020-02-11 18:37:14 -0800692
Srikrishna Iyer2a710a42020-02-10 10:39:15 -0800693 else:
694 log.info("No items specified to be run.")
Srikrishna Iyer4f0b0902020-01-25 02:28:47 -0800695
Srikrishna Iyer442d8db2020-03-05 15:17:17 -0800696 # Exit with non-zero status if there were errors or failures.
697 if cfg.has_errors():
698 log.error("Errors were encountered in this run.")
699 sys.exit(1)
700
Srikrishna Iyer09a81e92019-12-30 10:47:57 -0800701
702if __name__ == '__main__':
703 main()