siliconcompiler 0.26.5__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/__init__.py +24 -0
- siliconcompiler/__main__.py +12 -0
- siliconcompiler/_common.py +49 -0
- siliconcompiler/_metadata.py +36 -0
- siliconcompiler/apps/__init__.py +0 -0
- siliconcompiler/apps/_common.py +76 -0
- siliconcompiler/apps/sc.py +92 -0
- siliconcompiler/apps/sc_dashboard.py +94 -0
- siliconcompiler/apps/sc_issue.py +178 -0
- siliconcompiler/apps/sc_remote.py +199 -0
- siliconcompiler/apps/sc_server.py +39 -0
- siliconcompiler/apps/sc_show.py +142 -0
- siliconcompiler/apps/smake.py +232 -0
- siliconcompiler/checklists/__init__.py +0 -0
- siliconcompiler/checklists/oh_tapeout.py +41 -0
- siliconcompiler/core.py +3221 -0
- siliconcompiler/data/RobotoMono/LICENSE.txt +202 -0
- siliconcompiler/data/RobotoMono/RobotoMono-Regular.ttf +0 -0
- siliconcompiler/data/heartbeat.v +18 -0
- siliconcompiler/data/logo.png +0 -0
- siliconcompiler/flowgraph.py +570 -0
- siliconcompiler/flows/__init__.py +0 -0
- siliconcompiler/flows/_common.py +67 -0
- siliconcompiler/flows/asicflow.py +180 -0
- siliconcompiler/flows/asictopflow.py +38 -0
- siliconcompiler/flows/dvflow.py +86 -0
- siliconcompiler/flows/fpgaflow.py +202 -0
- siliconcompiler/flows/generate_openroad_rcx.py +66 -0
- siliconcompiler/flows/lintflow.py +35 -0
- siliconcompiler/flows/screenshotflow.py +51 -0
- siliconcompiler/flows/showflow.py +59 -0
- siliconcompiler/flows/signoffflow.py +53 -0
- siliconcompiler/flows/synflow.py +128 -0
- siliconcompiler/fpgas/__init__.py +0 -0
- siliconcompiler/fpgas/lattice_ice40.py +42 -0
- siliconcompiler/fpgas/vpr_example.py +109 -0
- siliconcompiler/issue.py +300 -0
- siliconcompiler/libs/__init__.py +0 -0
- siliconcompiler/libs/asap7sc7p5t.py +8 -0
- siliconcompiler/libs/gf180mcu.py +8 -0
- siliconcompiler/libs/nangate45.py +8 -0
- siliconcompiler/libs/sky130hd.py +8 -0
- siliconcompiler/libs/sky130io.py +8 -0
- siliconcompiler/package.py +412 -0
- siliconcompiler/pdks/__init__.py +0 -0
- siliconcompiler/pdks/asap7.py +8 -0
- siliconcompiler/pdks/freepdk45.py +8 -0
- siliconcompiler/pdks/gf180.py +8 -0
- siliconcompiler/pdks/skywater130.py +8 -0
- siliconcompiler/remote/__init__.py +36 -0
- siliconcompiler/remote/client.py +891 -0
- siliconcompiler/remote/schema.py +106 -0
- siliconcompiler/remote/server.py +507 -0
- siliconcompiler/remote/server_schema/requests/cancel_job.json +51 -0
- siliconcompiler/remote/server_schema/requests/check_progress.json +61 -0
- siliconcompiler/remote/server_schema/requests/check_server.json +38 -0
- siliconcompiler/remote/server_schema/requests/delete_job.json +51 -0
- siliconcompiler/remote/server_schema/requests/get_results.json +48 -0
- siliconcompiler/remote/server_schema/requests/remote_run.json +40 -0
- siliconcompiler/remote/server_schema/responses/cancel_job.json +18 -0
- siliconcompiler/remote/server_schema/responses/check_progress.json +30 -0
- siliconcompiler/remote/server_schema/responses/check_server.json +32 -0
- siliconcompiler/remote/server_schema/responses/delete_job.json +18 -0
- siliconcompiler/remote/server_schema/responses/get_results.json +21 -0
- siliconcompiler/remote/server_schema/responses/remote_run.json +25 -0
- siliconcompiler/report/__init__.py +13 -0
- siliconcompiler/report/html_report.py +74 -0
- siliconcompiler/report/report.py +355 -0
- siliconcompiler/report/streamlit_report.py +137 -0
- siliconcompiler/report/streamlit_viewer.py +944 -0
- siliconcompiler/report/summary_image.py +117 -0
- siliconcompiler/report/summary_table.py +105 -0
- siliconcompiler/report/utils.py +163 -0
- siliconcompiler/scheduler/__init__.py +2092 -0
- siliconcompiler/scheduler/docker_runner.py +253 -0
- siliconcompiler/scheduler/run_node.py +138 -0
- siliconcompiler/scheduler/send_messages.py +178 -0
- siliconcompiler/scheduler/slurm.py +208 -0
- siliconcompiler/scheduler/validation/email_credentials.json +54 -0
- siliconcompiler/schema/__init__.py +7 -0
- siliconcompiler/schema/schema_cfg.py +4014 -0
- siliconcompiler/schema/schema_obj.py +1841 -0
- siliconcompiler/schema/utils.py +93 -0
- siliconcompiler/sphinx_ext/__init__.py +0 -0
- siliconcompiler/sphinx_ext/dynamicgen.py +1006 -0
- siliconcompiler/sphinx_ext/schemagen.py +221 -0
- siliconcompiler/sphinx_ext/utils.py +166 -0
- siliconcompiler/targets/__init__.py +0 -0
- siliconcompiler/targets/asap7_demo.py +68 -0
- siliconcompiler/targets/asic_demo.py +38 -0
- siliconcompiler/targets/fpgaflow_demo.py +47 -0
- siliconcompiler/targets/freepdk45_demo.py +59 -0
- siliconcompiler/targets/gf180_demo.py +77 -0
- siliconcompiler/targets/skywater130_demo.py +70 -0
- siliconcompiler/templates/email/general.j2 +66 -0
- siliconcompiler/templates/email/summary.j2 +43 -0
- siliconcompiler/templates/issue/README.txt +26 -0
- siliconcompiler/templates/issue/run.sh +6 -0
- siliconcompiler/templates/report/bootstrap.min.css +7 -0
- siliconcompiler/templates/report/bootstrap.min.js +7 -0
- siliconcompiler/templates/report/bootstrap_LICENSE.md +24 -0
- siliconcompiler/templates/report/sc_report.j2 +427 -0
- siliconcompiler/templates/slurm/run.sh +9 -0
- siliconcompiler/templates/tcl/manifest.tcl.j2 +137 -0
- siliconcompiler/tools/__init__.py +0 -0
- siliconcompiler/tools/_common/__init__.py +432 -0
- siliconcompiler/tools/_common/asic.py +115 -0
- siliconcompiler/tools/_common/sdc/sc_constraints.sdc +76 -0
- siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +63 -0
- siliconcompiler/tools/bambu/bambu.py +32 -0
- siliconcompiler/tools/bambu/convert.py +77 -0
- siliconcompiler/tools/bluespec/bluespec.py +40 -0
- siliconcompiler/tools/bluespec/convert.py +103 -0
- siliconcompiler/tools/builtin/_common.py +155 -0
- siliconcompiler/tools/builtin/builtin.py +26 -0
- siliconcompiler/tools/builtin/concatenate.py +85 -0
- siliconcompiler/tools/builtin/join.py +27 -0
- siliconcompiler/tools/builtin/maximum.py +46 -0
- siliconcompiler/tools/builtin/minimum.py +57 -0
- siliconcompiler/tools/builtin/mux.py +70 -0
- siliconcompiler/tools/builtin/nop.py +38 -0
- siliconcompiler/tools/builtin/verify.py +83 -0
- siliconcompiler/tools/chisel/SCDriver.scala +10 -0
- siliconcompiler/tools/chisel/build.sbt +27 -0
- siliconcompiler/tools/chisel/chisel.py +37 -0
- siliconcompiler/tools/chisel/convert.py +140 -0
- siliconcompiler/tools/execute/exec_input.py +41 -0
- siliconcompiler/tools/execute/execute.py +17 -0
- siliconcompiler/tools/genfasm/bitstream.py +61 -0
- siliconcompiler/tools/genfasm/genfasm.py +40 -0
- siliconcompiler/tools/ghdl/convert.py +87 -0
- siliconcompiler/tools/ghdl/ghdl.py +41 -0
- siliconcompiler/tools/icarus/compile.py +87 -0
- siliconcompiler/tools/icarus/icarus.py +36 -0
- siliconcompiler/tools/icepack/bitstream.py +20 -0
- siliconcompiler/tools/icepack/icepack.py +43 -0
- siliconcompiler/tools/klayout/export.py +117 -0
- siliconcompiler/tools/klayout/klayout.py +119 -0
- siliconcompiler/tools/klayout/klayout_export.py +205 -0
- siliconcompiler/tools/klayout/klayout_operations.py +363 -0
- siliconcompiler/tools/klayout/klayout_show.py +242 -0
- siliconcompiler/tools/klayout/klayout_utils.py +176 -0
- siliconcompiler/tools/klayout/operations.py +194 -0
- siliconcompiler/tools/klayout/screenshot.py +98 -0
- siliconcompiler/tools/klayout/show.py +101 -0
- siliconcompiler/tools/magic/drc.py +49 -0
- siliconcompiler/tools/magic/extspice.py +19 -0
- siliconcompiler/tools/magic/magic.py +85 -0
- siliconcompiler/tools/magic/sc_drc.tcl +96 -0
- siliconcompiler/tools/magic/sc_extspice.tcl +54 -0
- siliconcompiler/tools/magic/sc_magic.tcl +47 -0
- siliconcompiler/tools/montage/montage.py +30 -0
- siliconcompiler/tools/montage/tile.py +66 -0
- siliconcompiler/tools/netgen/count_lvs.py +132 -0
- siliconcompiler/tools/netgen/lvs.py +90 -0
- siliconcompiler/tools/netgen/netgen.py +36 -0
- siliconcompiler/tools/netgen/sc_lvs.tcl +46 -0
- siliconcompiler/tools/nextpnr/apr.py +24 -0
- siliconcompiler/tools/nextpnr/nextpnr.py +59 -0
- siliconcompiler/tools/openfpgaloader/openfpgaloader.py +39 -0
- siliconcompiler/tools/openroad/__init__.py +0 -0
- siliconcompiler/tools/openroad/cts.py +45 -0
- siliconcompiler/tools/openroad/dfm.py +66 -0
- siliconcompiler/tools/openroad/export.py +131 -0
- siliconcompiler/tools/openroad/floorplan.py +70 -0
- siliconcompiler/tools/openroad/openroad.py +977 -0
- siliconcompiler/tools/openroad/physyn.py +27 -0
- siliconcompiler/tools/openroad/place.py +41 -0
- siliconcompiler/tools/openroad/rcx_bench.py +95 -0
- siliconcompiler/tools/openroad/rcx_extract.py +34 -0
- siliconcompiler/tools/openroad/route.py +45 -0
- siliconcompiler/tools/openroad/screenshot.py +60 -0
- siliconcompiler/tools/openroad/scripts/sc_apr.tcl +499 -0
- siliconcompiler/tools/openroad/scripts/sc_cts.tcl +64 -0
- siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +20 -0
- siliconcompiler/tools/openroad/scripts/sc_export.tcl +98 -0
- siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +413 -0
- siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +158 -0
- siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +7 -0
- siliconcompiler/tools/openroad/scripts/sc_place.tcl +84 -0
- siliconcompiler/tools/openroad/scripts/sc_procs.tcl +423 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +63 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx_bench.tcl +20 -0
- siliconcompiler/tools/openroad/scripts/sc_rcx_extract.tcl +12 -0
- siliconcompiler/tools/openroad/scripts/sc_route.tcl +133 -0
- siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +21 -0
- siliconcompiler/tools/openroad/scripts/sc_write.tcl +5 -0
- siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +361 -0
- siliconcompiler/tools/openroad/show.py +94 -0
- siliconcompiler/tools/openroad/templates/pex.tcl +8 -0
- siliconcompiler/tools/opensta/__init__.py +101 -0
- siliconcompiler/tools/opensta/report_libraries.py +28 -0
- siliconcompiler/tools/opensta/scripts/sc_procs.tcl +47 -0
- siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +74 -0
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +268 -0
- siliconcompiler/tools/opensta/timing.py +214 -0
- siliconcompiler/tools/slang/__init__.py +49 -0
- siliconcompiler/tools/slang/lint.py +101 -0
- siliconcompiler/tools/surelog/__init__.py +123 -0
- siliconcompiler/tools/surelog/parse.py +183 -0
- siliconcompiler/tools/surelog/templates/output.v +7 -0
- siliconcompiler/tools/sv2v/convert.py +46 -0
- siliconcompiler/tools/sv2v/sv2v.py +37 -0
- siliconcompiler/tools/template/template.py +125 -0
- siliconcompiler/tools/verilator/compile.py +139 -0
- siliconcompiler/tools/verilator/lint.py +19 -0
- siliconcompiler/tools/verilator/parse.py +27 -0
- siliconcompiler/tools/verilator/verilator.py +172 -0
- siliconcompiler/tools/vivado/__init__.py +7 -0
- siliconcompiler/tools/vivado/bitstream.py +21 -0
- siliconcompiler/tools/vivado/place.py +21 -0
- siliconcompiler/tools/vivado/route.py +21 -0
- siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +6 -0
- siliconcompiler/tools/vivado/scripts/sc_place.tcl +2 -0
- siliconcompiler/tools/vivado/scripts/sc_route.tcl +4 -0
- siliconcompiler/tools/vivado/scripts/sc_run.tcl +45 -0
- siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +25 -0
- siliconcompiler/tools/vivado/syn_fpga.py +20 -0
- siliconcompiler/tools/vivado/vivado.py +147 -0
- siliconcompiler/tools/vpr/_json_constraint.py +63 -0
- siliconcompiler/tools/vpr/_xml_constraint.py +109 -0
- siliconcompiler/tools/vpr/place.py +137 -0
- siliconcompiler/tools/vpr/route.py +124 -0
- siliconcompiler/tools/vpr/screenshot.py +54 -0
- siliconcompiler/tools/vpr/show.py +88 -0
- siliconcompiler/tools/vpr/vpr.py +357 -0
- siliconcompiler/tools/xyce/xyce.py +36 -0
- siliconcompiler/tools/yosys/lec.py +56 -0
- siliconcompiler/tools/yosys/prepareLib.py +59 -0
- siliconcompiler/tools/yosys/sc_lec.tcl +84 -0
- siliconcompiler/tools/yosys/sc_syn.tcl +79 -0
- siliconcompiler/tools/yosys/syn_asic.py +565 -0
- siliconcompiler/tools/yosys/syn_asic.tcl +377 -0
- siliconcompiler/tools/yosys/syn_asic_fpga_shared.tcl +31 -0
- siliconcompiler/tools/yosys/syn_fpga.py +146 -0
- siliconcompiler/tools/yosys/syn_fpga.tcl +233 -0
- siliconcompiler/tools/yosys/syn_strategies.tcl +81 -0
- siliconcompiler/tools/yosys/techmaps/lcu_kogge_stone.v +39 -0
- siliconcompiler/tools/yosys/templates/abc.const +2 -0
- siliconcompiler/tools/yosys/yosys.py +147 -0
- siliconcompiler/units.py +259 -0
- siliconcompiler/use.py +177 -0
- siliconcompiler/utils/__init__.py +423 -0
- siliconcompiler/utils/asic.py +158 -0
- siliconcompiler/utils/showtools.py +25 -0
- siliconcompiler-0.26.5.dist-info/LICENSE +190 -0
- siliconcompiler-0.26.5.dist-info/METADATA +195 -0
- siliconcompiler-0.26.5.dist-info/RECORD +251 -0
- siliconcompiler-0.26.5.dist-info/WHEEL +5 -0
- siliconcompiler-0.26.5.dist-info/entry_points.txt +12 -0
- siliconcompiler-0.26.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
import pandas
|
|
2
|
+
import os
|
|
3
|
+
from siliconcompiler import Schema
|
|
4
|
+
from siliconcompiler.report import utils
|
|
5
|
+
from siliconcompiler.flowgraph import nodes_to_execute
|
|
6
|
+
from siliconcompiler.tools._common import get_tool_task
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def make_metric_dataframe(chip):
|
|
10
|
+
'''
|
|
11
|
+
Returns a pandas dataframe to display in the data metrics table. All nodes
|
|
12
|
+
(steps and indices) are included on the x-axis while the metrics tracked
|
|
13
|
+
are on the y-axis. The y-axis row headers are in the form of a tuple where
|
|
14
|
+
the first element is the metric tracked and the second element is the unit.
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
>>> make_metric_dataframe(chip)
|
|
21
|
+
Returns pandas dataframe of tracked metrics.
|
|
22
|
+
'''
|
|
23
|
+
nodes, errors, metrics, metrics_unit, metrics_to_show, reports = utils._collect_data(chip)
|
|
24
|
+
# converts from 2d dictionary to pandas DataFrame, transposes so
|
|
25
|
+
# orientation is correct, and filters based on the metrics we track
|
|
26
|
+
data = (pandas.DataFrame.from_dict(metrics, orient='index').transpose())
|
|
27
|
+
data = data.loc[metrics_to_show]
|
|
28
|
+
# include metrics_unit
|
|
29
|
+
data.index = data.index.map(lambda x: (x, metrics_unit[x]))
|
|
30
|
+
return data
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def get_flowgraph_nodes(chip, step, index):
|
|
34
|
+
'''
|
|
35
|
+
Returns a dictionary to display in the data metrics table. One node
|
|
36
|
+
(step and index) is included on the x-axis while all the metrics tracked
|
|
37
|
+
are on the y-axis. Removes all key value pairs where the value is None.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
41
|
+
step (string) : Step of node.
|
|
42
|
+
index (string) : Index of node.
|
|
43
|
+
|
|
44
|
+
Example:
|
|
45
|
+
>>> get_flowgraph_nodes(chip, [(import, 0), (syn, 0)])
|
|
46
|
+
Returns pandas dataframe of tracked metrics.
|
|
47
|
+
'''
|
|
48
|
+
nodes = {}
|
|
49
|
+
tool, task = get_tool_task(chip, step, index)
|
|
50
|
+
if tool is not None:
|
|
51
|
+
nodes['tool'] = tool
|
|
52
|
+
if task is not None:
|
|
53
|
+
nodes['task'] = task
|
|
54
|
+
for key in chip.getkeys('record'):
|
|
55
|
+
if chip.get('record', key, field='pernode') == 'never':
|
|
56
|
+
value = chip.get('record', key)
|
|
57
|
+
else:
|
|
58
|
+
value = chip.get('record', key, step=step, index=index)
|
|
59
|
+
if value is not None:
|
|
60
|
+
if key == 'inputnode':
|
|
61
|
+
value = ", ".join([f'{step}{index}' for step, index in value])
|
|
62
|
+
nodes[key] = str(value)
|
|
63
|
+
return nodes
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def get_flowgraph_edges(chip):
|
|
67
|
+
'''
|
|
68
|
+
Returns a dicitionary where each key is one node, a tuple in the form
|
|
69
|
+
(step, index) and the value of each key is a set of tuples in the form
|
|
70
|
+
(step, index). The value of each key represents all the nodes that are
|
|
71
|
+
inputs to the key node.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
75
|
+
|
|
76
|
+
Example:
|
|
77
|
+
>>> get_flowgraph_edges(chip)
|
|
78
|
+
Returns dictionary where the values of the keys are the edges.
|
|
79
|
+
'''
|
|
80
|
+
flowgraph_edges = {}
|
|
81
|
+
flow = chip.get('option', 'flow')
|
|
82
|
+
for step in chip.getkeys('flowgraph', flow):
|
|
83
|
+
for index in chip.getkeys('flowgraph', flow, step):
|
|
84
|
+
flowgraph_edges[step, index] = set()
|
|
85
|
+
for in_step, in_index in chip.get('flowgraph', flow, step, index, 'input'):
|
|
86
|
+
flowgraph_edges[step, index].add((in_step, in_index))
|
|
87
|
+
return flowgraph_edges
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
|
|
91
|
+
'''
|
|
92
|
+
Function is a helper function to make_manifest. It mutates the input json.
|
|
93
|
+
|
|
94
|
+
Args:
|
|
95
|
+
manifest_subsect (dict) : Represents a subset of the original manifest.
|
|
96
|
+
modified_manifest_subsect (dict) : Represents a subset of the original
|
|
97
|
+
manifest, modified for readability.
|
|
98
|
+
|
|
99
|
+
Example:
|
|
100
|
+
>>> make_manifest_helper(manifest_subsection, {})
|
|
101
|
+
Mutates second paramaeter to remove simplify leaf nodes and remove
|
|
102
|
+
'default' nodes.
|
|
103
|
+
'''
|
|
104
|
+
|
|
105
|
+
def build_leaf(manifest_subsect):
|
|
106
|
+
if manifest_subsect['pernode'] == 'never':
|
|
107
|
+
if Schema.GLOBAL_KEY in manifest_subsect['node']:
|
|
108
|
+
value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
|
|
109
|
+
else:
|
|
110
|
+
value = manifest_subsect['node']['default']['default']['value']
|
|
111
|
+
return value
|
|
112
|
+
else:
|
|
113
|
+
nodes = manifest_subsect['node']
|
|
114
|
+
node_values = {}
|
|
115
|
+
for step in nodes:
|
|
116
|
+
if step == 'default' or step == Schema.GLOBAL_KEY:
|
|
117
|
+
value = nodes[step][step]['value']
|
|
118
|
+
node_values[step] = value
|
|
119
|
+
else:
|
|
120
|
+
for index in nodes[step]:
|
|
121
|
+
value = nodes[step][index]['value']
|
|
122
|
+
if value is None:
|
|
123
|
+
continue
|
|
124
|
+
if index == 'default' or index == Schema.GLOBAL_KEY:
|
|
125
|
+
node_values[step] = value
|
|
126
|
+
else:
|
|
127
|
+
node_values[step + index] = value
|
|
128
|
+
return node_values
|
|
129
|
+
|
|
130
|
+
if Schema._is_leaf(manifest_subsect):
|
|
131
|
+
if manifest_subsect['pernode'] == 'never':
|
|
132
|
+
if Schema.GLOBAL_KEY in manifest_subsect['node']:
|
|
133
|
+
value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
|
|
134
|
+
else:
|
|
135
|
+
value = manifest_subsect['node']['default']['default']['value']
|
|
136
|
+
modified_manifest_subsect['value'] = value
|
|
137
|
+
else:
|
|
138
|
+
nodes = manifest_subsect['node']
|
|
139
|
+
for step in nodes:
|
|
140
|
+
if step == 'default' or step == Schema.GLOBAL_KEY:
|
|
141
|
+
value = nodes[step][step]['value']
|
|
142
|
+
modified_manifest_subsect[step] = value
|
|
143
|
+
else:
|
|
144
|
+
for index in nodes[step]:
|
|
145
|
+
value = nodes[step][index]['value']
|
|
146
|
+
if value is None:
|
|
147
|
+
continue
|
|
148
|
+
if index == 'default' or index == Schema.GLOBAL_KEY:
|
|
149
|
+
modified_manifest_subsect[step] = value
|
|
150
|
+
else:
|
|
151
|
+
modified_manifest_subsect[step + index] = value
|
|
152
|
+
|
|
153
|
+
for key, key_dict in manifest_subsect.items():
|
|
154
|
+
if key != 'default':
|
|
155
|
+
if Schema._is_leaf(key_dict):
|
|
156
|
+
modified_manifest_subsect[key] = build_leaf(key_dict)
|
|
157
|
+
else:
|
|
158
|
+
modified_manifest_subsect[key] = {}
|
|
159
|
+
make_manifest_helper(key_dict, modified_manifest_subsect[key])
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def make_manifest(chip):
|
|
163
|
+
'''
|
|
164
|
+
Returns a dictionary of dictionaries/json
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
168
|
+
|
|
169
|
+
Example:
|
|
170
|
+
>>> make_manifest(chip)
|
|
171
|
+
Returns tree/json of manifest.
|
|
172
|
+
'''
|
|
173
|
+
manifest = chip.schema.cfg
|
|
174
|
+
modified_manifest = {}
|
|
175
|
+
make_manifest_helper(manifest, modified_manifest)
|
|
176
|
+
return modified_manifest
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def get_flowgraph_path(chip):
|
|
180
|
+
'''
|
|
181
|
+
Returns a set of all the nodes in the 'winning' path.
|
|
182
|
+
|
|
183
|
+
Args:
|
|
184
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
185
|
+
|
|
186
|
+
Example:
|
|
187
|
+
>>> get_flowgraph_path(chip)
|
|
188
|
+
Returns the "winning" path for that job.
|
|
189
|
+
'''
|
|
190
|
+
flow = chip.get('option', 'flow')
|
|
191
|
+
return utils._get_flowgraph_path(chip, flow, nodes_to_execute(chip))
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def search_manifest_keys(manifest, key):
|
|
195
|
+
'''
|
|
196
|
+
Function is a recursive helper to search_manifest, more info there.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
manifest (dictionary) : A dictionary representing the manifest.
|
|
200
|
+
key (string) : Searches all keys for partial matches on this string.
|
|
201
|
+
'''
|
|
202
|
+
filtered_manifest = {}
|
|
203
|
+
for dict_key in manifest:
|
|
204
|
+
if key in dict_key:
|
|
205
|
+
filtered_manifest[dict_key] = manifest[dict_key]
|
|
206
|
+
elif isinstance(manifest[dict_key], dict):
|
|
207
|
+
result = search_manifest_keys(manifest[dict_key], key)
|
|
208
|
+
if result: # result is non-empty
|
|
209
|
+
filtered_manifest[dict_key] = result
|
|
210
|
+
return filtered_manifest
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def search_manifest_values(manifest, value):
|
|
214
|
+
'''
|
|
215
|
+
Function is a recursive helper to search_manifest, more info there.
|
|
216
|
+
|
|
217
|
+
Args:
|
|
218
|
+
manifest (dictionary) : A dicitionary representing the manifest.
|
|
219
|
+
value (string) : Searches all values for partial matches on this
|
|
220
|
+
string.
|
|
221
|
+
'''
|
|
222
|
+
filtered_manifest = {}
|
|
223
|
+
for key in manifest:
|
|
224
|
+
if isinstance(manifest[key], dict):
|
|
225
|
+
result = search_manifest_values(manifest[key], value)
|
|
226
|
+
if result: # result is non-empty
|
|
227
|
+
filtered_manifest[key] = result
|
|
228
|
+
elif isinstance(manifest[key], str) and value in manifest[key]:
|
|
229
|
+
filtered_manifest[key] = manifest[key]
|
|
230
|
+
return filtered_manifest
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def search_manifest(manifest, key_search=None, value_search=None):
|
|
234
|
+
'''
|
|
235
|
+
Returns the same structure as make_manifest, but it is filtered by partial
|
|
236
|
+
matches by keys or values. If both key_search and value_search are None,
|
|
237
|
+
the original manifest is returned.
|
|
238
|
+
|
|
239
|
+
Args:
|
|
240
|
+
manifest (dictionary) : A dicitionary representing the manifest.
|
|
241
|
+
key_search (string) : Searches all keys for partial matches on this
|
|
242
|
+
string
|
|
243
|
+
value_search(string) : Searches all values for partial matches
|
|
244
|
+
on this string.
|
|
245
|
+
|
|
246
|
+
Example:
|
|
247
|
+
>>> search_manifest(jsonDict, key_search='input', value_search='v')
|
|
248
|
+
Returns a filtered version of jsonDict where each path contains at
|
|
249
|
+
least one key that contains the substring input and has values that
|
|
250
|
+
contain v.
|
|
251
|
+
'''
|
|
252
|
+
return_manifest = manifest
|
|
253
|
+
if key_search:
|
|
254
|
+
return_manifest = search_manifest_keys(return_manifest, key_search)
|
|
255
|
+
if value_search:
|
|
256
|
+
return_manifest = search_manifest_values(return_manifest, value_search)
|
|
257
|
+
return return_manifest
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
def get_total_manifest_key_count(manifest):
|
|
261
|
+
'''
|
|
262
|
+
Returns (int) the number of keys
|
|
263
|
+
|
|
264
|
+
Args:
|
|
265
|
+
manifest (dictionary) : A dicitionary representing the manifest.
|
|
266
|
+
acc (int) : An accumulator of the current number of folders and files.
|
|
267
|
+
'''
|
|
268
|
+
acc = len(manifest)
|
|
269
|
+
for dictKeys in manifest:
|
|
270
|
+
if isinstance(manifest[dictKeys], dict):
|
|
271
|
+
acc += get_total_manifest_key_count(manifest[dictKeys])
|
|
272
|
+
return acc
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def get_metrics_source(chip, step, index):
|
|
276
|
+
'''
|
|
277
|
+
Returns a dictionary where the keys are files in the logs and reports for
|
|
278
|
+
a given step and index. The values are a list of the metrics that come from
|
|
279
|
+
that file. If a file is not in the dictionary, that implies that no metrics
|
|
280
|
+
come from it.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
284
|
+
step (string) : Step of node.
|
|
285
|
+
index (string) : Index of node.
|
|
286
|
+
'''
|
|
287
|
+
file_to_metric = {}
|
|
288
|
+
tool, task = get_tool_task(chip, step, index)
|
|
289
|
+
metrics = chip.getkeys('tool', tool, 'task', task, 'report')
|
|
290
|
+
for metric in metrics:
|
|
291
|
+
sources = chip.get('tool', tool, 'task', task, 'report', metric, step=step, index=index)
|
|
292
|
+
for source in sources:
|
|
293
|
+
if source in file_to_metric:
|
|
294
|
+
file_to_metric[source].append(metric)
|
|
295
|
+
else:
|
|
296
|
+
file_to_metric[source] = [metric]
|
|
297
|
+
return file_to_metric
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def get_files(chip, step, index):
|
|
301
|
+
'''
|
|
302
|
+
Returns a list of 3-tuple that contain the path name of how to get to that
|
|
303
|
+
folder, the subfolders of that directory, and it's files. The list is
|
|
304
|
+
ordered by layer of directory.
|
|
305
|
+
|
|
306
|
+
Args:
|
|
307
|
+
chip (Chip) : The chip object that contains the schema read from.
|
|
308
|
+
step (string) : Step of node.
|
|
309
|
+
index (string) : Index of node.
|
|
310
|
+
'''
|
|
311
|
+
# could combine filters, but slightly more efficient to separate them
|
|
312
|
+
# Is remaking the list with sets instead of list worth it?
|
|
313
|
+
logs_and_reports = []
|
|
314
|
+
all_paths = os.walk(chip.getworkdir(step=step, index=index))
|
|
315
|
+
for path_name, folders, files in all_paths:
|
|
316
|
+
logs_and_reports.append((path_name, set(folders), set(files)))
|
|
317
|
+
return logs_and_reports
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def get_chart_data(chips, metric, nodes):
|
|
321
|
+
'''
|
|
322
|
+
Returns returns a a tuple where the first element is a 2d dictionary of
|
|
323
|
+
data points, following the forms {step+index: {chip_name: value}} where
|
|
324
|
+
each dictionary can have many keys. The second element is a string that represents the unit.
|
|
325
|
+
|
|
326
|
+
Args:
|
|
327
|
+
chips (list) : A list of dictionaries with the form
|
|
328
|
+
{'chip_object': chip, 'chip_name': name}.
|
|
329
|
+
metric (string) : The metric that the user is searching.
|
|
330
|
+
nodes (list) : A list of dictionaries with the form (step, index).
|
|
331
|
+
'''
|
|
332
|
+
metric_units = set() # the set of all units for this metric (hopefully, it's length is 0 or 1)
|
|
333
|
+
metric_datapoints = {}
|
|
334
|
+
metric_unit = ''
|
|
335
|
+
for chip_and_chip_name in chips:
|
|
336
|
+
chip = chip_and_chip_name['chip_object']
|
|
337
|
+
chip_name = chip_and_chip_name['chip_name']
|
|
338
|
+
nodes_list, errors, metrics, metrics_unit, metrics_to_show, reports = \
|
|
339
|
+
utils._collect_data(chip, format_as_string=False)
|
|
340
|
+
if metric in metrics_unit:
|
|
341
|
+
metric_unit = metrics_unit[metric]
|
|
342
|
+
metric_units.add(metric_unit)
|
|
343
|
+
for node in nodes:
|
|
344
|
+
if node not in metrics:
|
|
345
|
+
continue
|
|
346
|
+
value = metrics[node][metric]
|
|
347
|
+
if value is None:
|
|
348
|
+
continue
|
|
349
|
+
if node in metric_datapoints:
|
|
350
|
+
metric_datapoints[node][chip_name] = value
|
|
351
|
+
else:
|
|
352
|
+
metric_datapoints[node] = {chip_name: value}
|
|
353
|
+
if len(metric_units) > 1:
|
|
354
|
+
raise ValueError('Not all measurements were made with the same units')
|
|
355
|
+
return metric_datapoints, metric_unit
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import time
|
|
3
|
+
import tempfile
|
|
4
|
+
import json
|
|
5
|
+
|
|
6
|
+
from streamlit.web import bootstrap
|
|
7
|
+
from streamlit import config as _config
|
|
8
|
+
|
|
9
|
+
import multiprocessing
|
|
10
|
+
import subprocess
|
|
11
|
+
import atexit
|
|
12
|
+
import shutil
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Dashboard():
|
|
16
|
+
__port = 8501
|
|
17
|
+
|
|
18
|
+
def __init__(self, chip, port=None, graph_chips=None):
|
|
19
|
+
if not port:
|
|
20
|
+
port = Dashboard.__port
|
|
21
|
+
|
|
22
|
+
self.__dashboard = None
|
|
23
|
+
self.__chip = chip
|
|
24
|
+
self.__directory = tempfile.mkdtemp(prefix='sc_dashboard_',
|
|
25
|
+
suffix=f'_{self.__chip.design}')
|
|
26
|
+
self.__manifest = os.path.join(self.__directory, 'manifest.json')
|
|
27
|
+
self.__port = port
|
|
28
|
+
dirname = os.path.dirname(__file__)
|
|
29
|
+
self.__streamlit_file = os.path.join(dirname, 'streamlit_viewer.py')
|
|
30
|
+
|
|
31
|
+
self.__streamlit_args = [
|
|
32
|
+
("browser.gatherUsageStats", False),
|
|
33
|
+
("browser.serverPort", self.__port),
|
|
34
|
+
("logger.level", 'error'),
|
|
35
|
+
("runner.fastReruns", True),
|
|
36
|
+
("server.port", self.__port)
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
# pass in a json object called __graph_chips
|
|
40
|
+
# the key is the chip_name and value is the filepath
|
|
41
|
+
# if another argument is passed
|
|
42
|
+
|
|
43
|
+
# use of list is to preserve order
|
|
44
|
+
self.__graph_chips = []
|
|
45
|
+
self.__graph_chips_names = []
|
|
46
|
+
if graph_chips:
|
|
47
|
+
for chip_object_and_name in graph_chips:
|
|
48
|
+
chip_file_path = \
|
|
49
|
+
os.path.join(self.__directory,
|
|
50
|
+
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}
|
|
57
|
+
|
|
58
|
+
self.__sleep_time = 0.5
|
|
59
|
+
|
|
60
|
+
atexit.register(self.__cleanup)
|
|
61
|
+
|
|
62
|
+
def open_dashboard(self):
|
|
63
|
+
with open(self.__get_config_file(), 'w') as f:
|
|
64
|
+
json.dump(self.__config, f, indent=4)
|
|
65
|
+
|
|
66
|
+
self.update_manifest()
|
|
67
|
+
|
|
68
|
+
self.update_graph_manifests()
|
|
69
|
+
|
|
70
|
+
self.__dashboard = multiprocessing.Process(
|
|
71
|
+
target=self._run_streamlit_bootstrap)
|
|
72
|
+
|
|
73
|
+
self.__dashboard.start()
|
|
74
|
+
|
|
75
|
+
def update_manifest(self):
|
|
76
|
+
self.__chip.write_manifest(self.__manifest)
|
|
77
|
+
|
|
78
|
+
def update_graph_manifests(self):
|
|
79
|
+
for chip_object_and_name in self.__graph_chips:
|
|
80
|
+
chip = chip_object_and_name['chip']
|
|
81
|
+
file_path = chip_object_and_name['name']
|
|
82
|
+
chip.write_manifest(file_path)
|
|
83
|
+
|
|
84
|
+
def __get_config_file(self):
|
|
85
|
+
return os.path.join(self.__directory, 'config.json')
|
|
86
|
+
|
|
87
|
+
def is_running(self):
|
|
88
|
+
if self.__dashboard is None:
|
|
89
|
+
return False
|
|
90
|
+
|
|
91
|
+
if self.__dashboard.is_alive():
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
self.__dashboard = None
|
|
95
|
+
self.__manifest = None
|
|
96
|
+
return False
|
|
97
|
+
|
|
98
|
+
def stop(self):
|
|
99
|
+
if not self.is_running():
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
while self.__dashboard.is_alive():
|
|
103
|
+
self.__dashboard.terminate()
|
|
104
|
+
self._sleep()
|
|
105
|
+
|
|
106
|
+
self.__dashboard = None
|
|
107
|
+
self.__manifest = None
|
|
108
|
+
|
|
109
|
+
def wait(self):
|
|
110
|
+
self.__dashboard.join()
|
|
111
|
+
|
|
112
|
+
def _sleep(self):
|
|
113
|
+
time.sleep(self.__sleep_time)
|
|
114
|
+
|
|
115
|
+
def _run_streamlit_bootstrap(self):
|
|
116
|
+
for config, val in self.__streamlit_args:
|
|
117
|
+
_config.set_option(config, val)
|
|
118
|
+
|
|
119
|
+
bootstrap.run(self.__streamlit_file,
|
|
120
|
+
False,
|
|
121
|
+
[self.__get_config_file()],
|
|
122
|
+
flag_options={})
|
|
123
|
+
|
|
124
|
+
def __run_streamlit_subproc(self):
|
|
125
|
+
cmd = ['streamlit', 'run',
|
|
126
|
+
self.__streamlit_file, self.__get_config_file()]
|
|
127
|
+
for config, val in self.__streamlit_args:
|
|
128
|
+
cmd.append(f'--{config}')
|
|
129
|
+
cmd.append(val)
|
|
130
|
+
|
|
131
|
+
subprocess.Popen(cmd)
|
|
132
|
+
|
|
133
|
+
def __cleanup(self):
|
|
134
|
+
self.stop()
|
|
135
|
+
|
|
136
|
+
if os.path.exists(self.__directory):
|
|
137
|
+
shutil.rmtree(self.__directory)
|