siliconcompiler 0.28.1__py3-none-any.whl → 0.28.3__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 (140) hide show
  1. siliconcompiler/_common.py +12 -0
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/sc_dashboard.py +5 -1
  4. siliconcompiler/apps/sc_install.py +240 -0
  5. siliconcompiler/apps/sc_remote.py +1 -1
  6. siliconcompiler/core.py +54 -13
  7. siliconcompiler/remote/client.py +41 -10
  8. siliconcompiler/report/__init__.py +1 -1
  9. siliconcompiler/report/{streamlit_report.py → dashboard/__init__.py} +47 -10
  10. siliconcompiler/report/dashboard/components/__init__.py +534 -0
  11. siliconcompiler/report/dashboard/components/flowgraph.py +114 -0
  12. siliconcompiler/report/dashboard/components/graph.py +208 -0
  13. siliconcompiler/report/dashboard/layouts/__init__.py +20 -0
  14. siliconcompiler/report/dashboard/layouts/_common.py +43 -0
  15. siliconcompiler/report/dashboard/layouts/vertical_flowgraph.py +95 -0
  16. siliconcompiler/report/dashboard/layouts/vertical_flowgraph_node_tab.py +114 -0
  17. siliconcompiler/report/dashboard/layouts/vertical_flowgraph_sac_tabs.py +107 -0
  18. siliconcompiler/report/dashboard/state.py +215 -0
  19. siliconcompiler/report/dashboard/utils/__init__.py +73 -0
  20. siliconcompiler/report/dashboard/utils/file_utils.py +120 -0
  21. siliconcompiler/report/dashboard/viewer.py +36 -0
  22. siliconcompiler/report/report.py +22 -4
  23. siliconcompiler/scheduler/__init__.py +43 -6
  24. siliconcompiler/schema/schema_obj.py +4 -2
  25. siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +6 -5
  26. siliconcompiler/tools/openroad/floorplan.py +5 -0
  27. siliconcompiler/tools/openroad/openroad.py +12 -3
  28. siliconcompiler/tools/openroad/scripts/sc_cts.tcl +18 -13
  29. siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +6 -1
  30. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +1 -1
  31. siliconcompiler/tools/openroad/scripts/sc_procs.tcl +44 -16
  32. siliconcompiler/tools/openroad/scripts/sc_route.tcl +1 -1
  33. siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +10 -10
  34. siliconcompiler/tools/opensta/scripts/sc_procs.tcl +3 -3
  35. siliconcompiler/tools/yosys/syn_asic.tcl +1 -1
  36. siliconcompiler/toolscripts/_tools.json +136 -0
  37. siliconcompiler/toolscripts/_tools.py +222 -0
  38. siliconcompiler/toolscripts/rhel8/install-chisel.sh +26 -0
  39. siliconcompiler/toolscripts/rhel8/install-ghdl.sh +25 -0
  40. siliconcompiler/toolscripts/rhel8/install-icarus.sh +40 -0
  41. siliconcompiler/toolscripts/rhel8/install-klayout.sh +17 -0
  42. siliconcompiler/toolscripts/rhel8/install-magic.sh +26 -0
  43. siliconcompiler/toolscripts/rhel8/install-montage.sh +5 -0
  44. siliconcompiler/toolscripts/rhel8/install-netgen.sh +25 -0
  45. siliconcompiler/toolscripts/rhel8/install-openroad.sh +31 -0
  46. siliconcompiler/toolscripts/rhel8/install-slang.sh +31 -0
  47. siliconcompiler/toolscripts/rhel8/install-surelog.sh +32 -0
  48. siliconcompiler/toolscripts/rhel8/install-sv2v.sh +27 -0
  49. siliconcompiler/toolscripts/rhel8/install-verible.sh +24 -0
  50. siliconcompiler/toolscripts/rhel8/install-verilator.sh +40 -0
  51. siliconcompiler/toolscripts/rhel8/install-xyce.sh +64 -0
  52. siliconcompiler/toolscripts/rhel8/install-yosys.sh +23 -0
  53. siliconcompiler/toolscripts/rhel9/install-chisel.sh +26 -0
  54. siliconcompiler/toolscripts/rhel9/install-ghdl.sh +25 -0
  55. siliconcompiler/toolscripts/rhel9/install-icarus.sh +40 -0
  56. siliconcompiler/toolscripts/rhel9/install-klayout.sh +17 -0
  57. siliconcompiler/toolscripts/rhel9/install-magic.sh +26 -0
  58. siliconcompiler/toolscripts/rhel9/install-montage.sh +5 -0
  59. siliconcompiler/toolscripts/rhel9/install-netgen.sh +25 -0
  60. siliconcompiler/toolscripts/rhel9/install-slang.sh +31 -0
  61. siliconcompiler/toolscripts/rhel9/install-surelog.sh +32 -0
  62. siliconcompiler/toolscripts/rhel9/install-sv2v.sh +27 -0
  63. siliconcompiler/toolscripts/rhel9/install-verible.sh +24 -0
  64. siliconcompiler/toolscripts/rhel9/install-verilator.sh +40 -0
  65. siliconcompiler/toolscripts/rhel9/install-xdm.sh +43 -0
  66. siliconcompiler/toolscripts/rhel9/install-xyce.sh +64 -0
  67. siliconcompiler/toolscripts/rhel9/install-yosys.sh +23 -0
  68. siliconcompiler/toolscripts/ubuntu20/install-bambu.sh +49 -0
  69. siliconcompiler/toolscripts/ubuntu20/install-bluespec.sh +35 -0
  70. siliconcompiler/toolscripts/ubuntu20/install-chisel.sh +26 -0
  71. siliconcompiler/toolscripts/ubuntu20/install-ghdl.sh +25 -0
  72. siliconcompiler/toolscripts/ubuntu20/install-icarus.sh +25 -0
  73. siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +22 -0
  74. siliconcompiler/toolscripts/ubuntu20/install-klayout.sh +29 -0
  75. siliconcompiler/toolscripts/ubuntu20/install-magic.sh +24 -0
  76. siliconcompiler/toolscripts/ubuntu20/install-montage.sh +5 -0
  77. siliconcompiler/toolscripts/ubuntu20/install-netgen.sh +24 -0
  78. siliconcompiler/toolscripts/ubuntu20/install-nextpnr.sh +32 -0
  79. siliconcompiler/toolscripts/ubuntu20/install-openroad.sh +31 -0
  80. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +38 -0
  81. siliconcompiler/toolscripts/ubuntu20/install-slurm.sh +32 -0
  82. siliconcompiler/toolscripts/ubuntu20/install-surelog.sh +30 -0
  83. siliconcompiler/toolscripts/ubuntu20/install-sv2v.sh +26 -0
  84. siliconcompiler/toolscripts/ubuntu20/install-verible.sh +24 -0
  85. siliconcompiler/toolscripts/ubuntu20/install-verilator.sh +34 -0
  86. siliconcompiler/toolscripts/ubuntu20/install-vpr.sh +27 -0
  87. siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +40 -0
  88. siliconcompiler/toolscripts/ubuntu20/install-xyce.sh +65 -0
  89. siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +24 -0
  90. siliconcompiler/toolscripts/ubuntu22/install-bambu.sh +49 -0
  91. siliconcompiler/toolscripts/ubuntu22/install-bluespec.sh +35 -0
  92. siliconcompiler/toolscripts/ubuntu22/install-chisel.sh +26 -0
  93. siliconcompiler/toolscripts/ubuntu22/install-ghdl.sh +25 -0
  94. siliconcompiler/toolscripts/ubuntu22/install-icarus.sh +25 -0
  95. siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +22 -0
  96. siliconcompiler/toolscripts/ubuntu22/install-klayout.sh +29 -0
  97. siliconcompiler/toolscripts/ubuntu22/install-magic.sh +24 -0
  98. siliconcompiler/toolscripts/ubuntu22/install-montage.sh +5 -0
  99. siliconcompiler/toolscripts/ubuntu22/install-netgen.sh +24 -0
  100. siliconcompiler/toolscripts/ubuntu22/install-nextpnr.sh +32 -0
  101. siliconcompiler/toolscripts/ubuntu22/install-openroad.sh +31 -0
  102. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +28 -0
  103. siliconcompiler/toolscripts/ubuntu22/install-slurm.sh +32 -0
  104. siliconcompiler/toolscripts/ubuntu22/install-surelog.sh +25 -0
  105. siliconcompiler/toolscripts/ubuntu22/install-sv2v.sh +26 -0
  106. siliconcompiler/toolscripts/ubuntu22/install-verible.sh +24 -0
  107. siliconcompiler/toolscripts/ubuntu22/install-verilator.sh +34 -0
  108. siliconcompiler/toolscripts/ubuntu22/install-vpr.sh +27 -0
  109. siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +40 -0
  110. siliconcompiler/toolscripts/ubuntu22/install-xyce.sh +65 -0
  111. siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +24 -0
  112. siliconcompiler/toolscripts/ubuntu24/install-bambu.sh +49 -0
  113. siliconcompiler/toolscripts/ubuntu24/install-bluespec.sh +35 -0
  114. siliconcompiler/toolscripts/ubuntu24/install-chisel.sh +26 -0
  115. siliconcompiler/toolscripts/ubuntu24/install-ghdl.sh +25 -0
  116. siliconcompiler/toolscripts/ubuntu24/install-icarus.sh +25 -0
  117. siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +22 -0
  118. siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +31 -0
  119. siliconcompiler/toolscripts/ubuntu24/install-magic.sh +24 -0
  120. siliconcompiler/toolscripts/ubuntu24/install-montage.sh +5 -0
  121. siliconcompiler/toolscripts/ubuntu24/install-netgen.sh +24 -0
  122. siliconcompiler/toolscripts/ubuntu24/install-nextpnr.sh +32 -0
  123. siliconcompiler/toolscripts/ubuntu24/install-openroad.sh +31 -0
  124. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +28 -0
  125. siliconcompiler/toolscripts/ubuntu24/install-slurm.sh +32 -0
  126. siliconcompiler/toolscripts/ubuntu24/install-surelog.sh +25 -0
  127. siliconcompiler/toolscripts/ubuntu24/install-sv2v.sh +26 -0
  128. siliconcompiler/toolscripts/ubuntu24/install-verible.sh +24 -0
  129. siliconcompiler/toolscripts/ubuntu24/install-verilator.sh +34 -0
  130. siliconcompiler/toolscripts/ubuntu24/install-vpr.sh +27 -0
  131. siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +40 -0
  132. siliconcompiler/toolscripts/ubuntu24/install-xyce.sh +65 -0
  133. siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +24 -0
  134. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/METADATA +7 -6
  135. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/RECORD +139 -29
  136. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/entry_points.txt +1 -0
  137. siliconcompiler/report/streamlit_viewer.py +0 -944
  138. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/LICENSE +0 -0
  139. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/WHEEL +0 -0
  140. {siliconcompiler-0.28.1.dist-info → siliconcompiler-0.28.3.dist-info}/top_level.txt +0 -0
@@ -33,6 +33,18 @@ class NodeStatus():
33
33
  NodeStatus.PENDING,
34
34
  )
35
35
 
36
+ def is_success(status):
37
+ return status in (
38
+ NodeStatus.SUCCESS,
39
+ NodeStatus.SKIPPED
40
+ )
41
+
42
+ def is_error(status):
43
+ return status in (
44
+ NodeStatus.ERROR,
45
+ NodeStatus.TIMEOUT
46
+ )
47
+
36
48
 
37
49
  ###############################################################################
38
50
  # Package Customization classes
@@ -1,5 +1,5 @@
1
1
  # Version number following semver standard.
2
- version = '0.28.1'
2
+ version = '0.28.3'
3
3
 
4
4
  # Default server address for remote runs, if unspecified.
5
5
  default_server = 'https://server.siliconcompiler.com'
@@ -82,7 +82,11 @@ To include another chip object to compare to:
82
82
  raise ValueError(f'not a valid file path : {file_path}')
83
83
  graph_chip = siliconcompiler.core.Chip(design='')
84
84
  graph_chip.read_manifest(file_path)
85
- graph_chips.append({'chip': graph_chip, 'name': name})
85
+ graph_chips.append({
86
+ 'chip': graph_chip,
87
+ 'name': name,
88
+ 'cfg_path': os.path.abspath(file_path)
89
+ })
86
90
 
87
91
  chip._dashboard(wait=True, port=switches['port'], graph_chips=graph_chips)
88
92
 
@@ -0,0 +1,240 @@
1
+ # Copyright 2024 Silicon Compiler Authors. All Rights Reserved.
2
+
3
+ import argparse
4
+ import glob
5
+ import subprocess
6
+ import sys
7
+ import shutil
8
+ import re
9
+ import os.path
10
+ from collections.abc import Container
11
+ from pathlib import Path
12
+ import siliconcompiler
13
+ from siliconcompiler.scheduler import _get_machine_info
14
+
15
+
16
+ class ChoiceOptional(Container):
17
+ def __init__(self, choices):
18
+ super().__init__()
19
+
20
+ self.__choices = set(choices)
21
+
22
+ def __contains__(self, item):
23
+ if not item:
24
+ # allow empty value
25
+ return True
26
+ return item in self.__choices
27
+
28
+ def __iter__(self):
29
+ return self.__choices.__iter__()
30
+
31
+ def get_items(self, choices):
32
+ items = set(choices)
33
+ return sorted(list(items))
34
+
35
+
36
+ def install_tool(tool, script, build_dir, prefix):
37
+ # Ensure build dir is available
38
+ build_dir = Path(build_dir) / tool
39
+ shutil.rmtree(str(build_dir), ignore_errors=True)
40
+ build_dir.mkdir(parents=True, exist_ok=True)
41
+
42
+ # setup environment
43
+ env = os.environ.copy()
44
+ env["PREFIX"] = prefix
45
+
46
+ # run
47
+ ret = subprocess.call(script, env=env, cwd=build_dir)
48
+ if ret != 0:
49
+ print(f"Error occurred while building/installing {tool}")
50
+ return False
51
+ return True
52
+
53
+
54
+ def show_tool(tool, script):
55
+ def print_header(head):
56
+ border_len = max(80, len(script) + 2)
57
+ border = border_len*"#"
58
+ print(border)
59
+ print(f"# {tool} script / {head}")
60
+ if head == "start":
61
+ print(f"# {script}")
62
+ print(border)
63
+
64
+ print_header("start")
65
+
66
+ with open(script) as f:
67
+ for line in f:
68
+ print(line.rstrip())
69
+
70
+ print_header("end")
71
+
72
+
73
+ def _get_os_name():
74
+ machine_info = _get_machine_info()
75
+ system = machine_info.get('system', "").lower()
76
+ distro = machine_info.get('distro', "").lower()
77
+ osversion = machine_info.get('osversion', "").lower()
78
+ if system == 'linux':
79
+ if distro == 'ubuntu':
80
+ version, _ = osversion.split('.')
81
+ return f"{distro}{version}"
82
+ elif distro == 'rocky':
83
+ version, _ = osversion.split('.')
84
+ return f"rhel{version}"
85
+ elif distro == 'rhel':
86
+ version, _ = osversion.split('.')
87
+ return f"rhel{version}"
88
+ return None
89
+
90
+
91
+ def print_machine_info():
92
+ machine_info = _get_machine_info()
93
+ mapped_os = _get_os_name()
94
+
95
+ print("System: ", machine_info.get('system', None))
96
+ print("Distro: ", machine_info.get('distro', None))
97
+ print("Version: ", machine_info.get('osversion', None))
98
+ print("Mapped OS:", mapped_os)
99
+ print("Scripts: ", _get_tool_script_dir())
100
+
101
+
102
+ def _get_tool_script_dir():
103
+ return Path(siliconcompiler.__file__).parent / "toolscripts"
104
+
105
+
106
+ def _get_tools_list():
107
+ tools_root = _get_tool_script_dir()
108
+
109
+ script_dir = None
110
+ os_dir = _get_os_name()
111
+ if os_dir:
112
+ script_dir = tools_root / os_dir
113
+ if not script_dir.exists():
114
+ script_dir = None
115
+
116
+ tools = {}
117
+ if script_dir:
118
+ for script in glob.glob(str(script_dir / "install-*.sh")):
119
+ tool = re.match(r"install-(.*)\.sh", os.path.basename(script).lower())
120
+ tools[tool.group(1)] = script
121
+
122
+ return tools
123
+
124
+
125
+ def _recommended_tool_groups(tools):
126
+ groups = {
127
+ "asic": {"surelog", "sv2v", "yosys", "openroad", "klayout"},
128
+ "fpga": {"surelog", "sv2v", "yosys", "vpr"},
129
+ "digital-simulation": {"verilator", "icarus"},
130
+ "analog-simulation": {"xyce"}
131
+ }
132
+
133
+ filter_groups = {}
134
+ for group, group_tools in groups.items():
135
+ if all([tool in tools for tool in group_tools]):
136
+ filter_groups[group] = group_tools
137
+ return filter_groups
138
+
139
+
140
+ def main():
141
+ progname = "sc-install"
142
+ description = """
143
+ -----------------------------------------------------------
144
+ SC app install supported tools.
145
+
146
+ To install a single tool:
147
+ sc-install openroad
148
+
149
+ To install multiple tools:
150
+ sc-install openroad yosys klayout
151
+
152
+ To install a group of tools:
153
+ sc-install -group asic
154
+
155
+ To install tools in a different location:
156
+ sc-install -prefix /usr/local yosys
157
+
158
+ To build tools in a different location:
159
+ sc-install -build_dir /tmp yosys
160
+
161
+ To show the install script:
162
+ sc-install -show openroad
163
+
164
+ To system debugging information (this should only be used to debug):
165
+ sc-install -debug_machine
166
+ -----------------------------------------------------------
167
+ """
168
+ parser = argparse.ArgumentParser(
169
+ prog=progname,
170
+ description=description,
171
+ formatter_class=argparse.RawDescriptionHelpFormatter)
172
+
173
+ tools = _get_tools_list()
174
+
175
+ tool_choices = ChoiceOptional(tools.keys())
176
+ parser.add_argument(
177
+ "tool",
178
+ nargs="*",
179
+ choices=tool_choices,
180
+ help="tool to install")
181
+
182
+ tool_groups = _recommended_tool_groups(tools)
183
+ parser.add_argument(
184
+ "-group",
185
+ nargs="+",
186
+ choices=tool_groups.keys(),
187
+ help=f"tool group to install{' - not supported' if not tool_groups else ''}")
188
+
189
+ parser.add_argument(
190
+ "-prefix",
191
+ default=Path.home() / ".local",
192
+ help="Prefix to use when installing tool",
193
+ metavar="<path>")
194
+
195
+ parser.add_argument(
196
+ "-build_dir",
197
+ default=Path.home() / ".sc" / "tool_build",
198
+ help="Directory to build the tool in",
199
+ metavar="<path>")
200
+
201
+ parser.add_argument(
202
+ "-show",
203
+ action="store_true",
204
+ help="Show the install script and exit")
205
+
206
+ parser.add_argument(
207
+ "-debug_machine",
208
+ action="store_true",
209
+ help="Show information about this machine and exit")
210
+
211
+ args = parser.parse_args()
212
+
213
+ if args.debug_machine:
214
+ print_machine_info()
215
+ return 0
216
+
217
+ if not args.tool:
218
+ args.tool = []
219
+
220
+ args.tool = list(args.tool)
221
+ if args.group:
222
+ for group in args.group:
223
+ args.tool.extend(tool_groups[group])
224
+
225
+ tools_handled = set()
226
+ for tool in args.tool:
227
+ if tool in tools_handled:
228
+ continue
229
+ tools_handled.add(tool)
230
+ if args.show:
231
+ show_tool(tool, tools[tool])
232
+ else:
233
+ if not install_tool(tool, tools[tool], args.build_dir, args.prefix):
234
+ return 1
235
+
236
+ return 0
237
+
238
+
239
+ if __name__ == "__main__":
240
+ sys.exit(main())
@@ -185,7 +185,7 @@ To delete a job, use:
185
185
  # If only a manifest is specified, make a 'check_progress/' request and report results:
186
186
  elif chip_cfg:
187
187
  try:
188
- check_progress(chip)
188
+ check_progress(chip, [], {})
189
189
  except SiliconCompilerError as e:
190
190
  chip.logger.error(f'{e}')
191
191
  return 1
siliconcompiler/core.py CHANGED
@@ -88,6 +88,9 @@ class Chip:
88
88
  # Cache of file hashes
89
89
  self.__hashes = {}
90
90
 
91
+ # Dashboard
92
+ self._dash = None
93
+
91
94
  # Showtools
92
95
  self._showtools = {}
93
96
  for plugin in utils.get_plugins('show'):
@@ -758,7 +761,7 @@ class Chip:
758
761
  return fullstr
759
762
 
760
763
  ###########################################################################
761
- def valid(self, *keypath, default_valid=False, job=None):
764
+ def valid(self, *keypath, default_valid=False, job=None, check_complete=False):
762
765
  """
763
766
  Checks validity of a keypath.
764
767
 
@@ -770,6 +773,7 @@ class Chip:
770
773
  keypaths as a wildcard. Defaults to False.
771
774
  job (str): Jobname to use for dictionary access in place of the
772
775
  current active jobname.
776
+ check_complete (bool): Require the keypath be a complete path.
773
777
 
774
778
  Returns:
775
779
  Boolean indicating validity of keypath.
@@ -782,7 +786,10 @@ class Chip:
782
786
  >>> check = chip.valid('metric', 'foo', '0', 'tasktime', default_valid=True)
783
787
  Returns True, even if "foo" and "0" aren't in current configuration.
784
788
  """
785
- return self.schema.valid(*keypath, default_valid=default_valid, job=job)
789
+ return self.schema.valid(*keypath,
790
+ default_valid=default_valid,
791
+ job=job,
792
+ check_complete=check_complete)
786
793
 
787
794
  ###########################################################################
788
795
  def get(self, *keypath, field='value', job=None, step=None, index=None):
@@ -1108,6 +1115,24 @@ class Chip:
1108
1115
  Performs a lookup in the io map for the fileset and filetype
1109
1116
  and will use those if they are not provided in the arguments
1110
1117
  '''
1118
+ # Handle list inputs
1119
+ if isinstance(filename, (list, tuple)):
1120
+ for file in filename:
1121
+ self._add_input_output(
1122
+ category,
1123
+ file,
1124
+ fileset=fileset,
1125
+ filetype=filetype,
1126
+ iomap=iomap,
1127
+ step=step,
1128
+ index=index,
1129
+ package=package,
1130
+ quiet=quiet)
1131
+ return
1132
+
1133
+ if filename is None:
1134
+ raise ValueError(f"{category} cannot process None")
1135
+
1111
1136
  # Normalize value to string in case we receive a pathlib.Path
1112
1137
  filename = str(filename)
1113
1138
 
@@ -1132,8 +1157,9 @@ class Chip:
1132
1157
  use_filetype = filetype
1133
1158
 
1134
1159
  if not use_fileset or not use_filetype:
1135
- self.logger.error(f'Unable to infer {category} fileset and/or filetype for '
1136
- f'{filename} based on file extension.')
1160
+ raise SiliconCompilerError(
1161
+ f'Unable to infer {category} fileset and/or filetype for '
1162
+ f'{filename} based on file extension.')
1137
1163
  elif not quiet:
1138
1164
  if not fileset and not filetype:
1139
1165
  self.logger.info(f'{filename} inferred as {use_fileset}/{use_filetype}')
@@ -2695,18 +2721,25 @@ class Chip:
2695
2721
  >>> chip._dashboard()
2696
2722
  Opens a sesison of the dashboard.
2697
2723
  '''
2698
- dash = Dashboard(self, port=port, graph_chips=graph_chips)
2699
- dash.open_dashboard()
2724
+ if self._dash:
2725
+ # Remove previous dashboard
2726
+ self._dash.stop()
2727
+ self._dash = None
2728
+
2729
+ self._dash = Dashboard(self, port=port, graph_chips=graph_chips)
2730
+ self._dash.open_dashboard()
2731
+
2700
2732
  if wait:
2701
2733
  try:
2702
- dash.wait()
2734
+ self._dash.wait()
2703
2735
  except KeyboardInterrupt:
2704
- dash._sleep()
2736
+ self._dash._sleep()
2705
2737
  finally:
2706
- dash.stop()
2738
+ self._dash.stop()
2739
+ self._dash = None
2707
2740
  return None
2708
2741
 
2709
- return dash
2742
+ return self._dash
2710
2743
 
2711
2744
  ###########################################################################
2712
2745
  def summary(self, show_all_indices=False, generate_image=True, generate_html=True):
@@ -2746,8 +2779,8 @@ class Chip:
2746
2779
  work_dir = self.getworkdir()
2747
2780
  if os.path.isdir(work_dir):
2748
2781
  # Mark file paths where the reports can be found if they were generated.
2749
- results_html = os.path.join(work_dir, 'report.html')
2750
2782
  results_img = os.path.join(work_dir, f'{self.design}.png')
2783
+ results_html = os.path.join(work_dir, 'report.html')
2751
2784
 
2752
2785
  if generate_image:
2753
2786
  _generate_summary_image(self, results_img)
@@ -2755,13 +2788,18 @@ class Chip:
2755
2788
  if generate_html:
2756
2789
  _generate_html_report(self, flow, nodes_to_execute, results_html)
2757
2790
 
2791
+ # dashboard does not generate any data
2792
+ self.logger.info(f'Dashboard at "sc-dashboard -cfg {work_dir}/{self.design}.pkg.json"')
2793
+
2758
2794
  # Try to open the results and layout only if '-nodisplay' is not set.
2759
- # Priority: PNG, PDF, HTML.
2760
- if (not self.get('option', 'nodisplay')):
2795
+ # Priority: PNG > HTML > dashboard.
2796
+ if not self.get('option', 'nodisplay'):
2761
2797
  if os.path.isfile(results_img):
2762
2798
  _open_summary_image(results_img)
2763
2799
  elif os.path.isfile(results_html):
2764
2800
  _open_html_report(self, results_html)
2801
+ else:
2802
+ self._dashboard(wait=False)
2765
2803
 
2766
2804
  ###########################################################################
2767
2805
  def clock(self, pin, period, jitter=0, mode='global'):
@@ -3258,6 +3296,9 @@ class Chip:
3258
3296
  # Modules are not serializable, so save without cache
3259
3297
  attributes['_showtools'] = {}
3260
3298
 
3299
+ # Dashboard is not serializable
3300
+ attributes['_dash'] = None
3301
+
3261
3302
  # We have to remove the chip's logger before serializing the object
3262
3303
  # since the logger object is not serializable.
3263
3304
  del attributes['logger']
@@ -178,7 +178,7 @@ def _log_truncated_stats(chip, status, nodes_with_status, nodes_to_print):
178
178
 
179
179
 
180
180
  ###################################
181
- def _process_progress_info(chip, progress_info, nodes_to_print=3):
181
+ def _process_progress_info(chip, progress_info, recorded_nodes, all_nodes, nodes_to_print=3):
182
182
  '''
183
183
  Helper method to log information about a remote run's progress,
184
184
  based on information returned from a 'check_progress/' call.
@@ -201,6 +201,11 @@ def _process_progress_info(chip, progress_info, nodes_to_print=3):
201
201
  # collect completed
202
202
  completed.append(node)
203
203
 
204
+ if node in all_nodes:
205
+ step, index = all_nodes[node]
206
+ if (step, index) not in recorded_nodes:
207
+ chip.set('record', 'status', status, step=step, index=index)
208
+
204
209
  nodes_to_log = {key: nodes_to_log[key] for key in sorted(nodes_to_log.keys())}
205
210
 
206
211
  # Log information about the job's progress.
@@ -360,11 +365,32 @@ def __remote_run_loop(chip, check_interval):
360
365
  completed = []
361
366
  result_procs = []
362
367
 
368
+ recorded = []
369
+
363
370
  for step, index in nodes_to_execute(chip):
364
371
  if SCNodeStatus.is_done(chip.get('record', 'status', step=step, index=index)):
365
372
  continue
366
373
  all_nodes[f'{step}{index}'] = (step, index)
367
374
 
375
+ def import_manifests():
376
+ changed = False
377
+ for step, index in all_nodes.values():
378
+ if (step, index) in recorded:
379
+ continue
380
+
381
+ manifest = os.path.join(chip.getworkdir(step=step, index=index),
382
+ 'outputs',
383
+ f'{chip.design}.pkg.json')
384
+ if os.path.exists(manifest):
385
+ try:
386
+ chip.schema.read_journal(manifest)
387
+ recorded.append((step, index))
388
+ changed = True
389
+ except: # noqa E722
390
+ # Import may fail if file is still getting written
391
+ pass
392
+ return changed
393
+
368
394
  def schedule_download(node):
369
395
  node_proc = multiprocessor.Process(target=fetch_results,
370
396
  args=(chip, node))
@@ -376,7 +402,12 @@ def __remote_run_loop(chip, check_interval):
376
402
 
377
403
  while is_busy:
378
404
  time.sleep(check_interval)
379
- new_completed, is_busy = check_progress(chip)
405
+ import_manifests()
406
+ new_completed, is_busy = check_progress(chip, recorded, all_nodes)
407
+
408
+ if chip._dash:
409
+ chip._dash.update_manifest()
410
+
380
411
  nodes_to_fetch = []
381
412
  for node in new_completed:
382
413
  if node not in completed:
@@ -400,26 +431,26 @@ def __remote_run_loop(chip, check_interval):
400
431
  proc.join()
401
432
 
402
433
  # Read in node manifests
403
- for step, index in all_nodes.values():
404
- manifest = os.path.join(chip.getworkdir(step=step, index=index),
405
- 'outputs',
406
- f'{chip.design}.pkg.json')
407
- if os.path.exists(manifest):
408
- chip.schema.read_journal(manifest)
434
+ import_manifests()
409
435
 
410
436
  # Un-set the 'remote' option to avoid from/to-based summary/show errors
411
437
  chip.unset('option', 'remote')
412
438
 
439
+ if chip._dash:
440
+ chip._dash.update_manifest()
441
+
413
442
 
414
443
  ###################################
415
- def check_progress(chip):
444
+ def check_progress(chip, recorded_nodes, all_nodes):
416
445
  try:
417
446
  is_busy_info = is_job_busy(chip)
418
447
  is_busy = is_busy_info['busy']
419
448
  completed = []
420
449
  if is_busy:
421
450
  completed = _process_progress_info(chip,
422
- is_busy_info)
451
+ is_busy_info,
452
+ recorded_nodes,
453
+ all_nodes)
423
454
  return completed, is_busy
424
455
  except Exception as e:
425
456
  # Sometimes an exception is raised if the request library cannot
@@ -1,7 +1,7 @@
1
1
  from .summary_image import _generate_summary_image, _open_summary_image
2
2
  from .html_report import _generate_html_report, _open_html_report
3
3
  from .summary_table import _show_summary_table
4
- from .streamlit_report import Dashboard
4
+ from .dashboard import Dashboard
5
5
 
6
6
  __all__ = [
7
7
  "_generate_summary_image",
@@ -10,11 +10,20 @@ import multiprocessing
10
10
  import subprocess
11
11
  import atexit
12
12
  import shutil
13
+ import fasteners
14
+ import signal
15
+
16
+ from siliconcompiler.report.dashboard import utils
13
17
 
14
18
 
15
19
  class Dashboard():
16
20
  __port = 8501
17
21
 
22
+ @staticmethod
23
+ def __signal_handler(signal, frame):
24
+ # used to avoid issues during shutdown
25
+ pass
26
+
18
27
  def __init__(self, chip, port=None, graph_chips=None):
19
28
  if not port:
20
29
  port = Dashboard.__port
@@ -24,16 +33,18 @@ class Dashboard():
24
33
  self.__directory = tempfile.mkdtemp(prefix='sc_dashboard_',
25
34
  suffix=f'_{self.__chip.design}')
26
35
  self.__manifest = os.path.join(self.__directory, 'manifest.json')
36
+ self.__manifest_lock = os.path.join(self.__directory, 'manifest.lock')
27
37
  self.__port = port
28
38
  dirname = os.path.dirname(__file__)
29
- self.__streamlit_file = os.path.join(dirname, 'streamlit_viewer.py')
39
+ self.__streamlit_file = os.path.join(dirname, 'viewer.py')
30
40
 
31
41
  self.__streamlit_args = [
32
42
  ("browser.gatherUsageStats", False),
33
43
  ("browser.serverPort", self.__port),
34
44
  ("logger.level", 'error'),
35
45
  ("runner.fastReruns", True),
36
- ("server.port", self.__port)
46
+ ("server.port", self.__port),
47
+ ("client.toolbarMode", "viewer")
37
48
  ]
38
49
 
39
50
  # pass in a json object called __graph_chips
@@ -42,20 +53,33 @@ class Dashboard():
42
53
 
43
54
  # use of list is to preserve order
44
55
  self.__graph_chips = []
45
- self.__graph_chips_names = []
56
+ graph_chips_config = []
46
57
  if graph_chips:
47
58
  for chip_object_and_name in graph_chips:
48
59
  chip_file_path = \
49
60
  os.path.join(self.__directory,
50
61
  f"{chip_object_and_name['name']}.json")
51
- self.__graph_chips.append({'chip': chip_object_and_name['chip'],
52
- 'name': chip_file_path})
53
- self.__graph_chips_names.append(chip_file_path)
54
-
55
- self.__config = {"manifest": self.__manifest,
56
- "graph_chips": self.__graph_chips_names}
62
+ self.__graph_chips.append({
63
+ 'chip': chip_object_and_name['chip'],
64
+ 'name': chip_file_path
65
+ })
66
+ graph_chips_config.append({
67
+ "path": chip_file_path,
68
+ "cwd": utils.get_chip_cwd(
69
+ chip_object_and_name['chip'],
70
+ chip_object_and_name['cfg_path'])
71
+ })
72
+
73
+ self.__config = {
74
+ "manifest": self.__manifest,
75
+ "lock": self.__manifest_lock,
76
+ "graph_chips": graph_chips_config
77
+ }
57
78
 
58
79
  self.__sleep_time = 0.5
80
+ self.__signal_handler = None
81
+
82
+ self.__lock = fasteners.InterProcessLock(self.__manifest_lock)
59
83
 
60
84
  atexit.register(self.__cleanup)
61
85
 
@@ -70,10 +94,19 @@ class Dashboard():
70
94
  self.__dashboard = multiprocessing.Process(
71
95
  target=self._run_streamlit_bootstrap)
72
96
 
97
+ self.__signal_handler = signal.signal(signal.SIGINT, Dashboard.__signal_handler)
98
+
73
99
  self.__dashboard.start()
74
100
 
75
101
  def update_manifest(self):
76
- self.__chip.write_manifest(self.__manifest)
102
+ if not self.__manifest:
103
+ return
104
+
105
+ new_file = f"{self.__manifest}.new.json"
106
+ self.__chip.write_manifest(new_file)
107
+
108
+ with self.__lock:
109
+ shutil.move(new_file, self.__manifest)
77
110
 
78
111
  def update_graph_manifests(self):
79
112
  for chip_object_and_name in self.__graph_chips:
@@ -103,8 +136,12 @@ class Dashboard():
103
136
  self.__dashboard.terminate()
104
137
  self._sleep()
105
138
 
139
+ if self.__signal_handler:
140
+ signal.signal(signal.SIGINT, self.__signal_handler)
141
+
106
142
  self.__dashboard = None
107
143
  self.__manifest = None
144
+ self.__signal_handler = None
108
145
 
109
146
  def wait(self):
110
147
  self.__dashboard.join()