siliconcompiler 0.28.2__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.
- siliconcompiler/_common.py +12 -0
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/sc_dashboard.py +5 -1
- siliconcompiler/apps/sc_install.py +61 -13
- siliconcompiler/apps/sc_remote.py +1 -1
- siliconcompiler/core.py +39 -13
- siliconcompiler/remote/client.py +41 -10
- siliconcompiler/report/__init__.py +1 -1
- siliconcompiler/report/{streamlit_report.py → dashboard/__init__.py} +47 -10
- siliconcompiler/report/dashboard/components/__init__.py +534 -0
- siliconcompiler/report/dashboard/components/flowgraph.py +114 -0
- siliconcompiler/report/dashboard/components/graph.py +208 -0
- siliconcompiler/report/dashboard/layouts/__init__.py +20 -0
- siliconcompiler/report/dashboard/layouts/_common.py +43 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph.py +95 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph_node_tab.py +114 -0
- siliconcompiler/report/dashboard/layouts/vertical_flowgraph_sac_tabs.py +107 -0
- siliconcompiler/report/dashboard/state.py +215 -0
- siliconcompiler/report/dashboard/utils/__init__.py +73 -0
- siliconcompiler/report/dashboard/utils/file_utils.py +120 -0
- siliconcompiler/report/dashboard/viewer.py +36 -0
- siliconcompiler/report/report.py +22 -4
- siliconcompiler/scheduler/__init__.py +43 -6
- siliconcompiler/schema/schema_obj.py +4 -2
- siliconcompiler/tools/openroad/floorplan.py +5 -0
- siliconcompiler/tools/openroad/openroad.py +12 -3
- siliconcompiler/tools/openroad/scripts/sc_cts.tcl +18 -13
- siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +6 -1
- siliconcompiler/tools/openroad/scripts/sc_procs.tcl +28 -0
- siliconcompiler/toolscripts/_tools.json +8 -3
- siliconcompiler/toolscripts/rhel8/install-chisel.sh +26 -0
- siliconcompiler/toolscripts/rhel8/install-ghdl.sh +25 -0
- siliconcompiler/toolscripts/rhel8/install-icarus.sh +40 -0
- siliconcompiler/toolscripts/rhel8/install-klayout.sh +17 -0
- siliconcompiler/toolscripts/rhel8/install-magic.sh +26 -0
- siliconcompiler/toolscripts/rhel8/install-montage.sh +5 -0
- siliconcompiler/toolscripts/rhel8/install-netgen.sh +25 -0
- siliconcompiler/toolscripts/rhel8/install-openroad.sh +31 -0
- siliconcompiler/toolscripts/rhel8/install-slang.sh +31 -0
- siliconcompiler/toolscripts/rhel8/install-surelog.sh +32 -0
- siliconcompiler/toolscripts/rhel8/install-sv2v.sh +27 -0
- siliconcompiler/toolscripts/rhel8/install-verible.sh +24 -0
- siliconcompiler/toolscripts/rhel8/install-verilator.sh +40 -0
- siliconcompiler/toolscripts/rhel8/install-xyce.sh +64 -0
- siliconcompiler/toolscripts/rhel8/install-yosys.sh +23 -0
- siliconcompiler/toolscripts/rhel9/install-chisel.sh +26 -0
- siliconcompiler/toolscripts/rhel9/install-ghdl.sh +25 -0
- siliconcompiler/toolscripts/rhel9/install-icarus.sh +40 -0
- siliconcompiler/toolscripts/rhel9/install-klayout.sh +17 -0
- siliconcompiler/toolscripts/rhel9/install-magic.sh +26 -0
- siliconcompiler/toolscripts/rhel9/install-montage.sh +5 -0
- siliconcompiler/toolscripts/rhel9/install-netgen.sh +25 -0
- siliconcompiler/toolscripts/rhel9/install-slang.sh +31 -0
- siliconcompiler/toolscripts/rhel9/install-surelog.sh +32 -0
- siliconcompiler/toolscripts/rhel9/install-sv2v.sh +27 -0
- siliconcompiler/toolscripts/rhel9/install-verible.sh +24 -0
- siliconcompiler/toolscripts/rhel9/install-verilator.sh +40 -0
- siliconcompiler/toolscripts/rhel9/install-xdm.sh +43 -0
- siliconcompiler/toolscripts/rhel9/install-xyce.sh +64 -0
- siliconcompiler/toolscripts/rhel9/install-yosys.sh +23 -0
- siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +2 -2
- siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +2 -2
- siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +1 -1
- siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +2 -0
- siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +40 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +2 -2
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/METADATA +7 -6
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/RECORD +76 -32
- siliconcompiler/report/streamlit_viewer.py +0 -944
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/LICENSE +0 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/WHEEL +0 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/entry_points.txt +0 -0
- {siliconcompiler-0.28.2.dist-info → siliconcompiler-0.28.3.dist-info}/top_level.txt +0 -0
siliconcompiler/_common.py
CHANGED
|
@@ -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
|
siliconcompiler/_metadata.py
CHANGED
|
@@ -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({
|
|
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
|
|
|
@@ -70,17 +70,46 @@ def show_tool(tool, script):
|
|
|
70
70
|
print_header("end")
|
|
71
71
|
|
|
72
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
|
+
|
|
73
106
|
def _get_tools_list():
|
|
74
|
-
tools_root =
|
|
107
|
+
tools_root = _get_tool_script_dir()
|
|
75
108
|
|
|
76
|
-
machine_info = _get_machine_info()
|
|
77
109
|
script_dir = None
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
script_dir = f"{machine_info['distro'].lower()}{version}"
|
|
82
|
-
if script_dir:
|
|
83
|
-
script_dir = tools_root / script_dir
|
|
110
|
+
os_dir = _get_os_name()
|
|
111
|
+
if os_dir:
|
|
112
|
+
script_dir = tools_root / os_dir
|
|
84
113
|
if not script_dir.exists():
|
|
85
114
|
script_dir = None
|
|
86
115
|
|
|
@@ -93,14 +122,20 @@ def _get_tools_list():
|
|
|
93
122
|
return tools
|
|
94
123
|
|
|
95
124
|
|
|
96
|
-
def _recommended_tool_groups():
|
|
97
|
-
|
|
125
|
+
def _recommended_tool_groups(tools):
|
|
126
|
+
groups = {
|
|
98
127
|
"asic": {"surelog", "sv2v", "yosys", "openroad", "klayout"},
|
|
99
128
|
"fpga": {"surelog", "sv2v", "yosys", "vpr"},
|
|
100
129
|
"digital-simulation": {"verilator", "icarus"},
|
|
101
130
|
"analog-simulation": {"xyce"}
|
|
102
131
|
}
|
|
103
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
|
+
|
|
104
139
|
|
|
105
140
|
def main():
|
|
106
141
|
progname = "sc-install"
|
|
@@ -125,6 +160,9 @@ To build tools in a different location:
|
|
|
125
160
|
|
|
126
161
|
To show the install script:
|
|
127
162
|
sc-install -show openroad
|
|
163
|
+
|
|
164
|
+
To system debugging information (this should only be used to debug):
|
|
165
|
+
sc-install -debug_machine
|
|
128
166
|
-----------------------------------------------------------
|
|
129
167
|
"""
|
|
130
168
|
parser = argparse.ArgumentParser(
|
|
@@ -141,11 +179,12 @@ To show the install script:
|
|
|
141
179
|
choices=tool_choices,
|
|
142
180
|
help="tool to install")
|
|
143
181
|
|
|
182
|
+
tool_groups = _recommended_tool_groups(tools)
|
|
144
183
|
parser.add_argument(
|
|
145
184
|
"-group",
|
|
146
185
|
nargs="+",
|
|
147
|
-
choices=
|
|
148
|
-
help="tool group to install")
|
|
186
|
+
choices=tool_groups.keys(),
|
|
187
|
+
help=f"tool group to install{' - not supported' if not tool_groups else ''}")
|
|
149
188
|
|
|
150
189
|
parser.add_argument(
|
|
151
190
|
"-prefix",
|
|
@@ -164,15 +203,24 @@ To show the install script:
|
|
|
164
203
|
action="store_true",
|
|
165
204
|
help="Show the install script and exit")
|
|
166
205
|
|
|
206
|
+
parser.add_argument(
|
|
207
|
+
"-debug_machine",
|
|
208
|
+
action="store_true",
|
|
209
|
+
help="Show information about this machine and exit")
|
|
210
|
+
|
|
167
211
|
args = parser.parse_args()
|
|
168
212
|
|
|
213
|
+
if args.debug_machine:
|
|
214
|
+
print_machine_info()
|
|
215
|
+
return 0
|
|
216
|
+
|
|
169
217
|
if not args.tool:
|
|
170
218
|
args.tool = []
|
|
171
219
|
|
|
172
220
|
args.tool = list(args.tool)
|
|
173
221
|
if args.group:
|
|
174
222
|
for group in args.group:
|
|
175
|
-
args.tool.extend(
|
|
223
|
+
args.tool.extend(tool_groups[group])
|
|
176
224
|
|
|
177
225
|
tools_handled = set()
|
|
178
226
|
for tool in args.tool:
|
|
@@ -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,
|
|
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):
|
|
@@ -1123,6 +1130,9 @@ class Chip:
|
|
|
1123
1130
|
quiet=quiet)
|
|
1124
1131
|
return
|
|
1125
1132
|
|
|
1133
|
+
if filename is None:
|
|
1134
|
+
raise ValueError(f"{category} cannot process None")
|
|
1135
|
+
|
|
1126
1136
|
# Normalize value to string in case we receive a pathlib.Path
|
|
1127
1137
|
filename = str(filename)
|
|
1128
1138
|
|
|
@@ -1147,8 +1157,9 @@ class Chip:
|
|
|
1147
1157
|
use_filetype = filetype
|
|
1148
1158
|
|
|
1149
1159
|
if not use_fileset or not use_filetype:
|
|
1150
|
-
|
|
1151
|
-
|
|
1160
|
+
raise SiliconCompilerError(
|
|
1161
|
+
f'Unable to infer {category} fileset and/or filetype for '
|
|
1162
|
+
f'{filename} based on file extension.')
|
|
1152
1163
|
elif not quiet:
|
|
1153
1164
|
if not fileset and not filetype:
|
|
1154
1165
|
self.logger.info(f'{filename} inferred as {use_fileset}/{use_filetype}')
|
|
@@ -2710,18 +2721,25 @@ class Chip:
|
|
|
2710
2721
|
>>> chip._dashboard()
|
|
2711
2722
|
Opens a sesison of the dashboard.
|
|
2712
2723
|
'''
|
|
2713
|
-
|
|
2714
|
-
|
|
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
|
+
|
|
2715
2732
|
if wait:
|
|
2716
2733
|
try:
|
|
2717
|
-
|
|
2734
|
+
self._dash.wait()
|
|
2718
2735
|
except KeyboardInterrupt:
|
|
2719
|
-
|
|
2736
|
+
self._dash._sleep()
|
|
2720
2737
|
finally:
|
|
2721
|
-
|
|
2738
|
+
self._dash.stop()
|
|
2739
|
+
self._dash = None
|
|
2722
2740
|
return None
|
|
2723
2741
|
|
|
2724
|
-
return
|
|
2742
|
+
return self._dash
|
|
2725
2743
|
|
|
2726
2744
|
###########################################################################
|
|
2727
2745
|
def summary(self, show_all_indices=False, generate_image=True, generate_html=True):
|
|
@@ -2761,8 +2779,8 @@ class Chip:
|
|
|
2761
2779
|
work_dir = self.getworkdir()
|
|
2762
2780
|
if os.path.isdir(work_dir):
|
|
2763
2781
|
# Mark file paths where the reports can be found if they were generated.
|
|
2764
|
-
results_html = os.path.join(work_dir, 'report.html')
|
|
2765
2782
|
results_img = os.path.join(work_dir, f'{self.design}.png')
|
|
2783
|
+
results_html = os.path.join(work_dir, 'report.html')
|
|
2766
2784
|
|
|
2767
2785
|
if generate_image:
|
|
2768
2786
|
_generate_summary_image(self, results_img)
|
|
@@ -2770,13 +2788,18 @@ class Chip:
|
|
|
2770
2788
|
if generate_html:
|
|
2771
2789
|
_generate_html_report(self, flow, nodes_to_execute, results_html)
|
|
2772
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
|
+
|
|
2773
2794
|
# Try to open the results and layout only if '-nodisplay' is not set.
|
|
2774
|
-
# Priority: PNG
|
|
2775
|
-
if
|
|
2795
|
+
# Priority: PNG > HTML > dashboard.
|
|
2796
|
+
if not self.get('option', 'nodisplay'):
|
|
2776
2797
|
if os.path.isfile(results_img):
|
|
2777
2798
|
_open_summary_image(results_img)
|
|
2778
2799
|
elif os.path.isfile(results_html):
|
|
2779
2800
|
_open_html_report(self, results_html)
|
|
2801
|
+
else:
|
|
2802
|
+
self._dashboard(wait=False)
|
|
2780
2803
|
|
|
2781
2804
|
###########################################################################
|
|
2782
2805
|
def clock(self, pin, period, jitter=0, mode='global'):
|
|
@@ -3273,6 +3296,9 @@ class Chip:
|
|
|
3273
3296
|
# Modules are not serializable, so save without cache
|
|
3274
3297
|
attributes['_showtools'] = {}
|
|
3275
3298
|
|
|
3299
|
+
# Dashboard is not serializable
|
|
3300
|
+
attributes['_dash'] = None
|
|
3301
|
+
|
|
3276
3302
|
# We have to remove the chip's logger before serializing the object
|
|
3277
3303
|
# since the logger object is not serializable.
|
|
3278
3304
|
del attributes['logger']
|
siliconcompiler/remote/client.py
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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 .
|
|
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, '
|
|
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
|
-
|
|
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({
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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.
|
|
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()
|