siliconcompiler 0.28.4__py3-none-any.whl → 0.28.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/_common.py +88 -56
  3. siliconcompiler/apps/sc.py +33 -14
  4. siliconcompiler/apps/sc_dashboard.py +16 -9
  5. siliconcompiler/apps/sc_show.py +17 -15
  6. siliconcompiler/apps/utils/summarize.py +47 -0
  7. siliconcompiler/core.py +20 -12
  8. siliconcompiler/flows/drcflow.py +13 -0
  9. siliconcompiler/flows/interposerflow.py +17 -0
  10. siliconcompiler/libs/interposer.py +8 -0
  11. siliconcompiler/pdks/interposer.py +8 -0
  12. siliconcompiler/remote/schema.py +11 -1
  13. siliconcompiler/remote/server.py +7 -2
  14. siliconcompiler/report/dashboard/__init__.py +10 -3
  15. siliconcompiler/scheduler/__init__.py +93 -0
  16. siliconcompiler/schema/schema_cfg.py +15 -3
  17. siliconcompiler/schema/schema_obj.py +51 -1
  18. siliconcompiler/targets/interposer_demo.py +56 -0
  19. siliconcompiler/templates/tcl/manifest.tcl.j2 +2 -0
  20. siliconcompiler/tools/klayout/export.py +7 -4
  21. siliconcompiler/tools/klayout/klayout_export.py +3 -0
  22. siliconcompiler/tools/klayout/klayout_utils.py +8 -2
  23. siliconcompiler/tools/openroad/metrics.py +44 -0
  24. siliconcompiler/tools/openroad/openroad.py +3 -0
  25. siliconcompiler/tools/openroad/rdlroute.py +97 -0
  26. siliconcompiler/tools/openroad/scripts/sc_apr.tcl +20 -22
  27. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +0 -169
  28. siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +184 -0
  29. siliconcompiler/tools/openroad/scripts/sc_report.tcl +170 -0
  30. siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +11 -1
  31. siliconcompiler/tools/xyce/__init__.py +1 -1
  32. siliconcompiler/toolscripts/_tools.json +3 -4
  33. siliconcompiler/toolscripts/rhel8/install-xyce.sh +4 -5
  34. siliconcompiler/toolscripts/rhel9/install-xyce.sh +4 -5
  35. siliconcompiler/toolscripts/ubuntu20/install-xyce.sh +5 -5
  36. siliconcompiler/toolscripts/ubuntu22/install-xyce.sh +2 -2
  37. siliconcompiler/toolscripts/ubuntu24/install-xyce.sh +2 -2
  38. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/METADATA +14 -12
  39. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/RECORD +43 -33
  40. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/WHEEL +1 -1
  41. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/LICENSE +0 -0
  42. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/entry_points.txt +0 -0
  43. {siliconcompiler-0.28.4.dist-info → siliconcompiler-0.28.6.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.28.4'
2
+ version = '0.28.6'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -1,7 +1,11 @@
1
- import glob
2
1
  import os
3
2
 
4
3
 
4
+ # TODO: this is a hack to get around design name requirement: since legal
5
+ # design names probably can't contain spaces, we can detect if it is unset.
6
+ UNSET_DESIGN = ' unset '
7
+
8
+
5
9
  def manifest_switches():
6
10
  '''
7
11
  Returns a list of manifest switches that can be used
@@ -14,63 +18,91 @@ def manifest_switches():
14
18
  '-jobname']
15
19
 
16
20
 
17
- def load_manifest(chip, src_file):
18
- manifest = None
19
- if (src_file is not None) and (not chip.get('option', 'cfg')):
20
- if not os.path.exists(src_file):
21
- chip.logger.error(f'{src_file} cannot be found.')
22
- return False
23
- # only autoload manifest if user doesn't supply manually
24
- manifest = _get_manifest(os.path.dirname(src_file))
25
- if not manifest:
26
- design = os.path.splitext(os.path.basename(src_file))[0]
27
- chip.logger.error(f'Unable to automatically find manifest for design {design}. '
28
- 'Please provide a manifest explicitly using -cfg.')
29
- return False
30
- elif not chip.get('option', 'cfg'):
31
- manifest = _get_manifest_from_design(chip)
32
- if not manifest:
33
- chip.logger.error(f'Could not find manifest for {chip.design}')
34
- return False
21
+ def _get_manifests(cwd):
22
+ manifests = {}
35
23
 
36
- if manifest:
37
- chip.logger.info(f'Loading manifest: {manifest}')
38
- chip.read_manifest(manifest)
39
- return True
40
-
41
-
42
- def _get_manifest(dirname, design='*'):
43
- # pkg.json file may have a different name from the design due to the entrypoint
44
- glob_paths = [os.path.join(dirname, f'{design}.pkg.json'),
45
- os.path.join(dirname, 'outputs', f'{design}.pkg.json')]
46
- manifest = None
47
- for path in glob_paths:
48
- manifest = glob.glob(path)
49
- if manifest:
50
- manifest = manifest[0]
51
- break
52
-
53
- if not manifest or not os.path.isfile(manifest):
24
+ def get_dirs(cwd):
25
+ dirs = []
26
+ for dirname in os.listdir(cwd):
27
+ fullpath = os.path.join(cwd, dirname)
28
+ if os.path.isdir(fullpath):
29
+ dirs.append((dirname, fullpath))
30
+ return dirs
31
+
32
+ for _, buildpath in get_dirs(cwd):
33
+ for design, designdir in get_dirs(buildpath):
34
+ for jobname, jobdir in get_dirs(designdir):
35
+ manifest = os.path.join(jobdir, f'{design}.pkg.json')
36
+ if os.path.isfile(manifest):
37
+ manifests[(design, jobname, None, None)] = manifest
38
+ for step, stepdir in get_dirs(jobdir):
39
+ for index, indexdir in get_dirs(stepdir):
40
+ manifest = os.path.join(indexdir, 'outputs', f'{design}.pkg.json')
41
+ if os.path.isfile(manifest):
42
+ manifests[(design, jobname, step, index)] = manifest
43
+ else:
44
+ manifest = os.path.join(indexdir, 'inputs', f'{design}.pkg.json')
45
+ if os.path.isfile(manifest):
46
+ manifests[(design, jobname, step, index)] = manifest
47
+
48
+ organized_manifest = {}
49
+ for (design, job, step, index), manifest in manifests.items():
50
+ jobs = organized_manifest.setdefault(design, {})
51
+ jobs.setdefault(job, {})[step, index] = os.path.abspath(manifest)
52
+
53
+ return organized_manifest
54
+
55
+
56
+ def pick_manifest_from_file(chip, src_file, all_manifests):
57
+ if src_file is None:
54
58
  return None
55
- return manifest
56
59
 
60
+ if not os.path.exists(src_file):
61
+ chip.logger.error(f'{src_file} cannot be found.')
62
+ return None
63
+
64
+ src_dir = os.path.abspath(os.path.dirname(src_file))
65
+ for _, jobs in all_manifests.items():
66
+ for _, nodes in jobs.items():
67
+ for manifest in nodes.values():
68
+ if src_dir == os.path.dirname(manifest):
69
+ return manifest
57
70
 
58
- def _get_manifest_from_design(chip):
59
- for jobname, step, index in [
60
- (chip.get('option', 'jobname'),
61
- chip.get('arg', 'step'),
62
- chip.get('arg', 'index')),
63
- (chip.get('option', 'jobname'),
64
- None,
65
- None),
66
- (chip.schema.get_default('option', 'jobname'),
67
- chip.get('arg', 'step'),
68
- chip.get('arg', 'index')),
69
- (chip.schema.get_default('option', 'jobname'),
70
- None,
71
- None)]:
72
- manifest = _get_manifest(chip.getworkdir(jobname=jobname, step=step, index=index))
73
-
74
- if manifest:
75
- return manifest
76
71
  return None
72
+
73
+
74
+ def pick_manifest(chip, src_file=None):
75
+ all_manifests = _get_manifests(os.getcwd())
76
+
77
+ manifest = pick_manifest_from_file(chip, src_file, all_manifests)
78
+ if manifest:
79
+ return manifest
80
+
81
+ if chip.design == UNSET_DESIGN:
82
+ if len(all_manifests) == 1:
83
+ chip.set('design', list(all_manifests.keys())[0])
84
+ else:
85
+ chip.logger.error('Design name is not set')
86
+ return None
87
+
88
+ if chip.design not in all_manifests:
89
+ chip.logger.error(f'Could not find manifest for {chip.design}')
90
+ return None
91
+
92
+ if chip.get('option', 'jobname') not in all_manifests[chip.design] and \
93
+ len(all_manifests[chip.design]) != 1:
94
+ chip.logger.error(f'Could not determine jobname for {chip.design}')
95
+ return None
96
+
97
+ jobname = chip.get('option', 'jobname')
98
+ if chip.get('option', 'jobname') not in all_manifests[chip.design]:
99
+ jobname = list(all_manifests[chip.design].keys())[0]
100
+
101
+ if (None, None) in all_manifests[chip.design][jobname]:
102
+ manifest = all_manifests[chip.design][jobname][None, None]
103
+ else:
104
+ # pick newest manifest
105
+ manifest = list(sorted(all_manifests[chip.design][jobname].values(),
106
+ key=lambda file: os.stat(file).st_ctime))[-1]
107
+
108
+ return manifest
@@ -10,6 +10,37 @@ from siliconcompiler.targets import skywater130_demo
10
10
  from siliconcompiler import SiliconCompilerError
11
11
 
12
12
 
13
+ def _infer_designname(chip):
14
+ topfile = None
15
+ sourcesets = chip.getkeys('input')
16
+ for sourceset in reversed(('rtl', 'hll')):
17
+ if sourceset in sourcesets:
18
+ sourcesets.remove(sourceset)
19
+ sourcesets.insert(0, sourceset)
20
+ for sourceset in sourcesets:
21
+ for filetype in chip.getkeys('input', sourceset):
22
+ all_vals = chip.schema._getvals('input', sourceset, filetype)
23
+ if all_vals:
24
+ # just look at first value
25
+ sources, _, _ = all_vals[0]
26
+ # grab first source
27
+ topfile = sources[0]
28
+ break
29
+ if topfile:
30
+ break
31
+
32
+ if not topfile:
33
+ return None
34
+
35
+ root = os.path.basename(topfile)
36
+ while True:
37
+ root, ext = os.path.splitext(root)
38
+ if not ext:
39
+ break
40
+
41
+ return root
42
+
43
+
13
44
  ###########################
14
45
  def main():
15
46
  progname = "sc"
@@ -50,24 +81,12 @@ def main():
50
81
 
51
82
  # Set design if none specified
52
83
  if chip.get('design') == UNSET_DESIGN:
53
- topfile = None
54
- for sourceset in ('rtl', 'hll'):
55
- for filetype in chip.getkeys('input', sourceset):
56
- all_vals = chip.schema._getvals('input', sourceset, filetype)
57
- if all_vals:
58
- # just look at first value
59
- sources, _, _ = all_vals[0]
60
- # grab first source
61
- topfile = sources[0]
62
- break
63
- if topfile:
64
- break
84
+ topmodule = _infer_designname(chip)
65
85
 
66
- if not topfile:
86
+ if not topmodule:
67
87
  chip.logger.error('Invalid arguments: either specify -design or provide sources.')
68
88
  return 1
69
89
 
70
- topmodule = os.path.splitext(os.path.basename(topfile))[0]
71
90
  chip.set('design', topmodule)
72
91
 
73
92
  # Set demo target if none specified
@@ -2,7 +2,7 @@
2
2
  import sys
3
3
  import siliconcompiler
4
4
  import os
5
- from siliconcompiler.apps._common import load_manifest, manifest_switches
5
+ from siliconcompiler.apps._common import pick_manifest, manifest_switches, UNSET_DESIGN
6
6
 
7
7
 
8
8
  def main():
@@ -11,9 +11,16 @@ def main():
11
11
  -----------------------------------------------------------
12
12
  SC app to open a dashboard for a given manifest.
13
13
 
14
- To open:
14
+ To open and allow sc-dashboard to autoload manifest:
15
+ sc-dashboard
16
+
17
+ To open by specifying manifest:
15
18
  sc-dashboard -cfg <path to manifest>
16
19
 
20
+ To open by specifying design and optionally jobname:
21
+ sc-dashboard -design <name>
22
+ sc-dashboard -design <name> -jobname <jobname>
23
+
17
24
  To specify a different port than the default:
18
25
  sc-dashboard -cfg <path to manifest> -port 10000
19
26
 
@@ -23,10 +30,6 @@ To include another chip object to compare to:
23
30
  -----------------------------------------------------------
24
31
  """
25
32
 
26
- # TODO: this is a hack to get around design name requirement: since legal
27
- # design names probably can't contain spaces, we can detect if it is unset.
28
- UNSET_DESIGN = ' unset '
29
-
30
33
  # Create a base chip class.
31
34
  chip = siliconcompiler.Chip(UNSET_DESIGN)
32
35
 
@@ -54,15 +57,19 @@ To include another chip object to compare to:
54
57
  chip.logger.error(e)
55
58
  return 1
56
59
 
60
+ if not chip.get('option', 'cfg'):
61
+ manifest = pick_manifest(chip)
62
+
63
+ if manifest:
64
+ chip.logger.info(f'Loading manifest: {manifest}')
65
+ chip.read_manifest(manifest)
66
+
57
67
  # Error checking
58
68
  design = chip.get('design')
59
69
  if design == UNSET_DESIGN:
60
70
  chip.logger.error('Design not loaded')
61
71
  return 1
62
72
 
63
- if not load_manifest(chip, None):
64
- return 1
65
-
66
73
  graph_chips = []
67
74
  if switches['graph_cfg']:
68
75
  for i, name_and_file_path in enumerate(switches['graph_cfg']):
@@ -3,7 +3,7 @@ import sys
3
3
  import os
4
4
  import siliconcompiler
5
5
  from siliconcompiler.utils import get_default_iomap
6
- from siliconcompiler.apps._common import load_manifest, manifest_switches
6
+ from siliconcompiler.apps._common import manifest_switches, pick_manifest, UNSET_DESIGN
7
7
  from siliconcompiler.utils import get_file_ext
8
8
 
9
9
 
@@ -18,6 +18,9 @@ def main():
18
18
 
19
19
  Examples:
20
20
 
21
+ sc-show
22
+ (displays build/adder/job0/write_gds/0/outputs/adder.gds)
23
+
21
24
  sc-show -design adder
22
25
  (displays build/adder/job0/write_gds/0/outputs/adder.gds)
23
26
 
@@ -40,10 +43,6 @@ def main():
40
43
  (displays build/adder/job0/route/1/outputs/adder.def)
41
44
  """
42
45
 
43
- # TODO: this is a hack to get around design name requirement: since legal
44
- # design names probably can't contain spaces, we can detect if it is unset.
45
- UNSET_DESIGN = ' unset '
46
-
47
46
  # Create a base chip class.
48
47
  chip = siliconcompiler.Chip(UNSET_DESIGN)
49
48
 
@@ -81,10 +80,6 @@ def main():
81
80
  chip.logger.error(e)
82
81
  return 1
83
82
 
84
- # Error checking
85
- design = chip.get('design')
86
- design_set = design != UNSET_DESIGN
87
-
88
83
  # Search input keys for files
89
84
  input_mode = []
90
85
  for fileset in chip.getkeys('input'):
@@ -92,11 +87,6 @@ def main():
92
87
  if chip.schema._getvals('input', fileset, mode):
93
88
  input_mode = [('input', fileset, mode)]
94
89
 
95
- if not (design_set or input_mode):
96
- chip.logger.error('Nothing to load: please define a target with '
97
- '-cfg, -design, and/or inputs.')
98
- return 1
99
-
100
90
  filename = None
101
91
  if input_mode:
102
92
  check_ext = list(chip._showtools.keys())
@@ -115,7 +105,19 @@ def main():
115
105
 
116
106
  filename = get_file_from_keys()
117
107
 
118
- if not load_manifest(chip, filename):
108
+ # Attempt to load a manifest
109
+ if not chip.get('option', 'cfg'):
110
+ manifest = pick_manifest(chip, src_file=filename)
111
+ if manifest:
112
+ chip.logger.info(f'Loading manifest: {manifest}')
113
+ chip.read_manifest(manifest)
114
+
115
+ # Error checking
116
+ design = chip.get('design')
117
+ design_set = design != UNSET_DESIGN
118
+ if not (design_set or input_mode):
119
+ chip.logger.error('Nothing to load: please define a target with '
120
+ '-cfg, -design, and/or inputs.')
119
121
  return 1
120
122
 
121
123
  # Read in file
@@ -0,0 +1,47 @@
1
+ # Copyright 2024 Silicon Compiler Authors. All Rights Reserved.
2
+
3
+ # Standard Modules
4
+ import sys
5
+
6
+ import siliconcompiler
7
+ from siliconcompiler.apps._common import UNSET_DESIGN
8
+ from siliconcompiler import SiliconCompilerError
9
+
10
+
11
+ ###########################
12
+ def main():
13
+ progname = "summarize"
14
+ description = """
15
+ ------------------------------------------------------------
16
+ Utility script to print job summary from a manifest
17
+ ------------------------------------------------------------
18
+ """
19
+ # Create a base chip class.
20
+ chip = siliconcompiler.Chip(UNSET_DESIGN)
21
+
22
+ # Read command-line inputs and generate Chip objects to run the flow on.
23
+ try:
24
+ chip.create_cmdline(progname,
25
+ description=description,
26
+ switchlist=['-cfg',
27
+ '-loglevel'])
28
+ except SiliconCompilerError:
29
+ return 1
30
+ except Exception as e:
31
+ chip.logger.error(e)
32
+ return 1
33
+
34
+ design = chip.get('design')
35
+ if design == UNSET_DESIGN:
36
+ chip.logger.error('Design not loaded')
37
+ return 1
38
+
39
+ # Print Job Summary
40
+ chip.summary(generate_image=False, generate_html=False)
41
+
42
+ return 0
43
+
44
+
45
+ #########################
46
+ if __name__ == "__main__":
47
+ sys.exit(main())
siliconcompiler/core.py CHANGED
@@ -399,7 +399,7 @@ class Chip:
399
399
  self.set(*key, packages, field='package', step=step, index=index)
400
400
 
401
401
  # Read in target if set
402
- if "target" in extra_params:
402
+ if extra_params is not None and "target" in extra_params:
403
403
  if extra_params["target"]:
404
404
  # running target command
405
405
  # Search order "{name}", and "siliconcompiler.targets.{name}"
@@ -416,7 +416,7 @@ class Chip:
416
416
  self.use(modules[0])
417
417
  extra_params["target"] = modules[0].__name__
418
418
 
419
- if "use" in extra_params:
419
+ if extra_params is not None and "use" in extra_params:
420
420
  if extra_params["use"]:
421
421
  for use in extra_params["use"]:
422
422
  mod = self._load_module(use)
@@ -437,19 +437,25 @@ class Chip:
437
437
  if "-target" in additional_args:
438
438
  raise ValueError('-target cannot be used as an additional argument')
439
439
 
440
- additional_args["-target"] = {
441
- "help": "target to load",
442
- "metavar": "<target>"
443
- }
440
+ if switchlist is None or '-target' in switchlist:
441
+ additional_args["-target"] = {
442
+ "help": "target to load",
443
+ "metavar": "<target>"
444
+ }
445
+ if switchlist:
446
+ switchlist.remove('-target')
444
447
 
445
448
  if "-use" in additional_args:
446
449
  raise ValueError('-use cannot be used as an additional argument')
447
450
 
448
- additional_args["-use"] = {
449
- "action": "append",
450
- "help": "modules to load",
451
- "metavar": "<module>"
452
- }
451
+ if switchlist is None or '-use' in switchlist:
452
+ additional_args["-use"] = {
453
+ "action": "append",
454
+ "help": "modules to load",
455
+ "metavar": "<module>"
456
+ }
457
+ if switchlist:
458
+ switchlist.remove('-use')
453
459
 
454
460
  try:
455
461
  return self.schema.create_cmdline(
@@ -2123,7 +2129,9 @@ class Chip:
2123
2129
  nonlocal graph_idx
2124
2130
 
2125
2131
  for subgraph in graph_info["graphs"]:
2132
+ child_prefix = prefix
2126
2133
  if get_node_count(graph_info["graphs"][subgraph]) > 1:
2134
+ child_prefix = f"{child_prefix}{subgraph}."
2127
2135
  graph = graphviz.Digraph(name=f"cluster_{graph_idx}")
2128
2136
  graph_idx += 1
2129
2137
 
@@ -2143,7 +2151,7 @@ class Chip:
2143
2151
  else:
2144
2152
  graph = parent
2145
2153
 
2146
- build_graph(graph_info["graphs"][subgraph], graph, f"{prefix}{subgraph}.")
2154
+ build_graph(graph_info["graphs"][subgraph], graph, child_prefix)
2147
2155
 
2148
2156
  if graph is not parent:
2149
2157
  parent.subgraph(graph)
@@ -0,0 +1,13 @@
1
+ from siliconcompiler import Flow
2
+
3
+ from siliconcompiler.tools.klayout import drc
4
+
5
+
6
+ def setup():
7
+ '''
8
+ Perform a DRC run on an input GDS
9
+ '''
10
+ flow = Flow('drcflow')
11
+ flow.node('drcflow', 'drc', drc)
12
+
13
+ return flow
@@ -0,0 +1,17 @@
1
+ from siliconcompiler import Flow
2
+
3
+ from siliconcompiler.tools.openroad import rdlroute
4
+ from siliconcompiler.tools.klayout import export
5
+
6
+
7
+ def setup():
8
+ '''
9
+ A flow to perform RDL routing and generate a GDS
10
+ '''
11
+ flow = Flow('interposerflow')
12
+ flow.node('interposerflow', 'rdlroute', rdlroute)
13
+ flow.node('interposerflow', 'write_gds', export)
14
+
15
+ flow.edge('interposerflow', 'rdlroute', 'write_gds')
16
+
17
+ return flow
@@ -0,0 +1,8 @@
1
+ import siliconcompiler
2
+ from lambdapdk.interposer.libs.bumps import setup
3
+
4
+
5
+ #########################
6
+ if __name__ == "__main__":
7
+ lib = setup(siliconcompiler.Chip('<lib>'))
8
+ lib.write_manifest(f'{lib.top()}.json')
@@ -0,0 +1,8 @@
1
+ import siliconcompiler
2
+ from lambdapdk.interposer import setup
3
+
4
+
5
+ #########################
6
+ if __name__ == "__main__":
7
+ pdk = setup(siliconcompiler.Chip('<lib>'))
8
+ pdk.write_manifest(f'{pdk.top()}.json')
@@ -2,7 +2,7 @@ from siliconcompiler.schema.schema_cfg import scparam
2
2
  from siliconcompiler.schema import Schema
3
3
 
4
4
 
5
- SCHEMA_VERSION = '0.0.1'
5
+ SCHEMA_VERSION = '0.0.2'
6
6
 
7
7
 
8
8
  def schema_cfg():
@@ -93,6 +93,16 @@ def schema_cfg():
93
93
  schelp="""
94
94
  Provides explicit control over the level of debug logging printed.""")
95
95
 
96
+ scparam(cfg, ['option', 'checkinterval'],
97
+ sctype='int',
98
+ defvalue=30,
99
+ shorthelp="Interval for client",
100
+ switch="-checkinterval <int>",
101
+ example=["cli: -checkinterval 10",
102
+ "api: chip.set('option', 'checkinterval', 10)"],
103
+ schelp="""
104
+ Interval between checks to announce to clients""")
105
+
96
106
  return cfg
97
107
 
98
108
 
@@ -239,7 +239,7 @@ class Server:
239
239
 
240
240
  # Return a response to the client.
241
241
  return web.json_response({'message': f"Starting job: {job_hash}",
242
- 'interval': 30,
242
+ 'interval': self.checkinterval,
243
243
  'job_hash': job_hash})
244
244
 
245
245
  ####################
@@ -375,7 +375,7 @@ class Server:
375
375
  'sc_schema': sc_schema_version,
376
376
  'sc_server': Server.__version__,
377
377
  },
378
- 'progress_interval': 30
378
+ 'progress_interval': self.checkinterval
379
379
  }
380
380
 
381
381
  username = job_params['username']
@@ -490,6 +490,11 @@ class Server:
490
490
  # Ensure that NFS mounting path is absolute.
491
491
  return os.path.abspath(self.get('option', 'nfsmount'))
492
492
 
493
+ ###################
494
+ @property
495
+ def checkinterval(self):
496
+ return self.get('option', 'checkinterval')
497
+
493
498
  def get(self, *keypath, field='value'):
494
499
  return self.schema.get(*keypath, field=field)
495
500
 
@@ -3,9 +3,6 @@ import time
3
3
  import tempfile
4
4
  import json
5
5
 
6
- from streamlit.web import bootstrap
7
- from streamlit import config as _config
8
-
9
6
  import multiprocessing
10
7
  import subprocess
11
8
  import atexit
@@ -16,6 +13,13 @@ import socketserver
16
13
 
17
14
  from siliconcompiler.report.dashboard import utils
18
15
 
16
+ try:
17
+ from streamlit.web import bootstrap
18
+ from streamlit import config as _config
19
+ except ModuleNotFoundError:
20
+ bootstrap = None
21
+ _config = None
22
+
19
23
 
20
24
  class Dashboard():
21
25
  __port = 8501
@@ -26,6 +30,9 @@ class Dashboard():
26
30
  pass
27
31
 
28
32
  def __init__(self, chip, port=None, graph_chips=None):
33
+ if not bootstrap:
34
+ raise NotImplementedError('streamlit is not available')
35
+
29
36
  if not port:
30
37
  port = Dashboard.get_next_port()
31
38
  if not port: