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,570 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import math
|
|
3
|
+
from siliconcompiler import SiliconCompilerError, NodeStatus
|
|
4
|
+
from siliconcompiler.tools._common import input_file_node_name, get_tool_task
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def _check_execution_nodes_inputs(chip, flow):
|
|
8
|
+
for node in nodes_to_execute(chip, flow):
|
|
9
|
+
if node in _get_execution_entry_nodes(chip, flow):
|
|
10
|
+
continue
|
|
11
|
+
pruned_node_inputs = set(_get_pruned_node_inputs(chip, flow, node))
|
|
12
|
+
node_inputs = set(_get_flowgraph_node_inputs(chip, flow, node))
|
|
13
|
+
tool, task = get_tool_task(chip, node[0], node[1], flow=flow)
|
|
14
|
+
if tool == 'builtin' and not pruned_node_inputs or \
|
|
15
|
+
tool != 'builtin' and pruned_node_inputs != node_inputs:
|
|
16
|
+
chip.logger.warning(
|
|
17
|
+
f'Flowgraph connection from {node_inputs.difference(pruned_node_inputs)} '
|
|
18
|
+
f'to {node} is missing. '
|
|
19
|
+
f'Double check your flowgraph and from/to/prune options.')
|
|
20
|
+
return False
|
|
21
|
+
return True
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _nodes_to_execute(chip, flow, from_nodes, to_nodes, prune_nodes):
|
|
25
|
+
'''
|
|
26
|
+
Assumes a flowgraph with valid edges for the inputs
|
|
27
|
+
'''
|
|
28
|
+
nodes_to_execute = []
|
|
29
|
+
for from_node in from_nodes:
|
|
30
|
+
for node in _nodes_to_execute_recursive(chip, flow, from_node, to_nodes, prune_nodes):
|
|
31
|
+
if node not in nodes_to_execute:
|
|
32
|
+
nodes_to_execute.append(node)
|
|
33
|
+
return nodes_to_execute
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def _nodes_to_execute_recursive(chip, flow, from_node, to_nodes, prune_nodes, path=[]):
|
|
37
|
+
path = path.copy()
|
|
38
|
+
nodes_to_execute = []
|
|
39
|
+
|
|
40
|
+
if from_node in prune_nodes:
|
|
41
|
+
return []
|
|
42
|
+
if from_node in path:
|
|
43
|
+
raise SiliconCompilerError(f'Path {path} would form a circle with {from_node}')
|
|
44
|
+
path.append(from_node)
|
|
45
|
+
|
|
46
|
+
if from_node in to_nodes:
|
|
47
|
+
for node in path:
|
|
48
|
+
nodes_to_execute.append(node)
|
|
49
|
+
for output_node in _get_flowgraph_node_outputs(chip, flow, from_node):
|
|
50
|
+
for node in _nodes_to_execute_recursive(chip, flow, output_node, to_nodes,
|
|
51
|
+
prune_nodes, path=path):
|
|
52
|
+
if node not in nodes_to_execute:
|
|
53
|
+
nodes_to_execute.append(node)
|
|
54
|
+
|
|
55
|
+
return nodes_to_execute
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _unreachable_steps_to_execute(chip, flow, cond=lambda _: True):
|
|
59
|
+
from_nodes = set(_get_execution_entry_nodes(chip, flow))
|
|
60
|
+
to_nodes = set(_get_execution_exit_nodes(chip, flow))
|
|
61
|
+
prune_nodes = chip.get('option', 'prune')
|
|
62
|
+
reachable_nodes = set(_reachable_flowgraph_nodes(chip, flow, from_nodes, cond=cond,
|
|
63
|
+
prune_nodes=prune_nodes))
|
|
64
|
+
unreachable_nodes = to_nodes.difference(reachable_nodes)
|
|
65
|
+
unreachable_steps = set()
|
|
66
|
+
for unreachable_node in unreachable_nodes:
|
|
67
|
+
if not any(filter(lambda node: node[0] == unreachable_node[0], reachable_nodes)):
|
|
68
|
+
unreachable_steps.add(unreachable_node[0])
|
|
69
|
+
return unreachable_steps
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _reachable_flowgraph_nodes(chip, flow, from_nodes, cond=lambda _: True, prune_nodes=[]):
|
|
73
|
+
visited_nodes = set()
|
|
74
|
+
current_nodes = from_nodes.copy()
|
|
75
|
+
while current_nodes:
|
|
76
|
+
current_nodes_copy = current_nodes.copy()
|
|
77
|
+
for current_node in current_nodes_copy:
|
|
78
|
+
if current_node in prune_nodes:
|
|
79
|
+
current_nodes.remove(current_node)
|
|
80
|
+
continue
|
|
81
|
+
if cond(current_node):
|
|
82
|
+
visited_nodes.add(current_node)
|
|
83
|
+
current_nodes.remove(current_node)
|
|
84
|
+
outputs = _get_flowgraph_node_outputs(chip, flow, current_node)
|
|
85
|
+
current_nodes.update(outputs)
|
|
86
|
+
if current_nodes == current_nodes_copy:
|
|
87
|
+
break
|
|
88
|
+
return visited_nodes
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_flowgraph_node_inputs(chip, flow, node):
|
|
92
|
+
step, index = node
|
|
93
|
+
inputs = set()
|
|
94
|
+
for in_node in chip.get('flowgraph', flow, step, index, 'input'):
|
|
95
|
+
if chip.get('record', 'status', step=in_node[0], index=in_node[1]) == \
|
|
96
|
+
NodeStatus.SKIPPED:
|
|
97
|
+
inputs.update(_get_flowgraph_node_inputs(chip, flow, in_node))
|
|
98
|
+
else:
|
|
99
|
+
inputs.add(in_node)
|
|
100
|
+
return list(inputs)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _get_pruned_flowgraph_nodes(chip, flow, prune_nodes):
|
|
104
|
+
# Ignore option from/to, we want reachable nodes of the whole flowgraph
|
|
105
|
+
from_nodes = set(_get_flowgraph_entry_nodes(chip, flow))
|
|
106
|
+
return _reachable_flowgraph_nodes(chip, flow, from_nodes, prune_nodes=prune_nodes)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _get_pruned_node_inputs(chip, flow, node):
|
|
110
|
+
prune_nodes = chip.get('option', 'prune')
|
|
111
|
+
pruned_flowgraph_nodes = _get_pruned_flowgraph_nodes(chip, flow, prune_nodes)
|
|
112
|
+
return list(filter(lambda node: node in pruned_flowgraph_nodes,
|
|
113
|
+
_get_flowgraph_node_inputs(chip, flow, node)))
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def _get_flowgraph_node_outputs(chip, flow, node):
|
|
117
|
+
node_outputs = []
|
|
118
|
+
|
|
119
|
+
iter_nodes = _get_flowgraph_nodes(chip, flow)
|
|
120
|
+
for iter_node in iter_nodes:
|
|
121
|
+
iter_node_inputs = chip.get('flowgraph', flow, *iter_node, 'input')
|
|
122
|
+
if node in iter_node_inputs:
|
|
123
|
+
node_outputs.append(iter_node)
|
|
124
|
+
|
|
125
|
+
return node_outputs
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _get_flowgraph_nodes(chip, flow, steps=None, indices=None):
|
|
129
|
+
nodes = []
|
|
130
|
+
for step in chip.getkeys('flowgraph', flow):
|
|
131
|
+
if steps and step not in steps:
|
|
132
|
+
continue
|
|
133
|
+
for index in chip.getkeys('flowgraph', flow, step):
|
|
134
|
+
if indices and index not in indices:
|
|
135
|
+
continue
|
|
136
|
+
nodes.append((step, index))
|
|
137
|
+
return nodes
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
#######################################
|
|
141
|
+
def _get_execution_entry_nodes(chip, flow):
|
|
142
|
+
if chip.get('arg', 'step') and chip.get('arg', 'index'):
|
|
143
|
+
return [(chip.get('arg', 'step'), chip.get('arg', 'index'))]
|
|
144
|
+
if chip.get('arg', 'step'):
|
|
145
|
+
return _get_flowgraph_nodes(chip, flow, steps=[chip.get('arg', 'step')])
|
|
146
|
+
# If we explicitly get the nodes for a flow other than the current one,
|
|
147
|
+
# Ignore the 'option' 'from'
|
|
148
|
+
if chip.get('option', 'flow') == flow and chip.get('option', 'from'):
|
|
149
|
+
return _get_flowgraph_nodes(chip, flow, steps=chip.get('option', 'from'))
|
|
150
|
+
return _get_flowgraph_entry_nodes(chip, flow)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _get_flowgraph_entry_nodes(chip, flow, steps=None):
|
|
154
|
+
'''
|
|
155
|
+
Collect all step/indices that represent the entry
|
|
156
|
+
nodes for the flowgraph
|
|
157
|
+
'''
|
|
158
|
+
nodes = []
|
|
159
|
+
for (step, index) in _get_flowgraph_nodes(chip, flow, steps=steps):
|
|
160
|
+
if not chip.get('flowgraph', flow, step, index, 'input'):
|
|
161
|
+
nodes.append((step, index))
|
|
162
|
+
return nodes
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
def _get_execution_exit_nodes(chip, flow):
|
|
166
|
+
if chip.get('arg', 'step') and chip.get('arg', 'index'):
|
|
167
|
+
return [(chip.get('arg', 'step'), chip.get('arg', 'index'))]
|
|
168
|
+
if chip.get('arg', 'step'):
|
|
169
|
+
return _get_flowgraph_nodes(chip, flow, steps=[chip.get('arg', 'step')])
|
|
170
|
+
# If we explicitly get the nodes for a flow other than the current one,
|
|
171
|
+
# Ignore the 'option' 'to'
|
|
172
|
+
if chip.get('option', 'flow') == flow and chip.get('option', 'to'):
|
|
173
|
+
return _get_flowgraph_nodes(chip, flow, steps=chip.get('option', 'to'))
|
|
174
|
+
return _get_flowgraph_exit_nodes(chip, flow)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
#######################################
|
|
178
|
+
def _get_flowgraph_exit_nodes(chip, flow, steps=None):
|
|
179
|
+
'''
|
|
180
|
+
Collect all step/indices that represent the exit
|
|
181
|
+
nodes for the flowgraph
|
|
182
|
+
'''
|
|
183
|
+
inputnodes = []
|
|
184
|
+
for (step, index) in _get_flowgraph_nodes(chip, flow, steps=steps):
|
|
185
|
+
inputnodes.extend(chip.get('flowgraph', flow, step, index, 'input'))
|
|
186
|
+
nodes = []
|
|
187
|
+
for (step, index) in _get_flowgraph_nodes(chip, flow, steps=steps):
|
|
188
|
+
if (step, index) not in inputnodes:
|
|
189
|
+
nodes.append((step, index))
|
|
190
|
+
return nodes
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
#######################################
|
|
194
|
+
def _get_flowgraph_execution_order(chip, flow, reverse=False):
|
|
195
|
+
'''
|
|
196
|
+
Generates a list of nodes in the order they will be executed.
|
|
197
|
+
'''
|
|
198
|
+
|
|
199
|
+
# Generate execution edges lookup map
|
|
200
|
+
ex_map = {}
|
|
201
|
+
for step, index in _get_flowgraph_nodes(chip, flow):
|
|
202
|
+
for istep, iindex in chip.get('flowgraph', flow, step, index, 'input'):
|
|
203
|
+
if reverse:
|
|
204
|
+
ex_map.setdefault((step, index), set()).add((istep, iindex))
|
|
205
|
+
else:
|
|
206
|
+
ex_map.setdefault((istep, iindex), set()).add((step, index))
|
|
207
|
+
|
|
208
|
+
# Collect execution order of nodes
|
|
209
|
+
if reverse:
|
|
210
|
+
order = [set(_get_flowgraph_exit_nodes(chip, flow))]
|
|
211
|
+
else:
|
|
212
|
+
order = [set(_get_flowgraph_entry_nodes(chip, flow))]
|
|
213
|
+
|
|
214
|
+
while True:
|
|
215
|
+
next_level = set()
|
|
216
|
+
for step, index in order[-1]:
|
|
217
|
+
if (step, index) in ex_map and \
|
|
218
|
+
not any([(step, index) in v for v in ex_map.values()]):
|
|
219
|
+
next_level.update(ex_map.pop((step, index)))
|
|
220
|
+
|
|
221
|
+
if not next_level:
|
|
222
|
+
break
|
|
223
|
+
|
|
224
|
+
order.append(next_level)
|
|
225
|
+
|
|
226
|
+
# Filter duplicates from flow
|
|
227
|
+
used_nodes = set()
|
|
228
|
+
exec_order = []
|
|
229
|
+
order.reverse()
|
|
230
|
+
for n, level_nodes in enumerate(order):
|
|
231
|
+
exec_order.append(list(level_nodes.difference(used_nodes)))
|
|
232
|
+
used_nodes.update(level_nodes)
|
|
233
|
+
|
|
234
|
+
exec_order.reverse()
|
|
235
|
+
|
|
236
|
+
return exec_order
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def get_executed_nodes(chip, flow):
|
|
240
|
+
from_nodes = _get_flowgraph_entry_nodes(chip, flow)
|
|
241
|
+
return get_nodes_from(chip, flow, from_nodes)
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def get_nodes_from(chip, flow, nodes):
|
|
245
|
+
to_nodes = _get_execution_exit_nodes(chip, flow)
|
|
246
|
+
return _nodes_to_execute(chip,
|
|
247
|
+
flow,
|
|
248
|
+
set(nodes),
|
|
249
|
+
set(to_nodes),
|
|
250
|
+
set(chip.get('option', 'prune')))
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
###########################################################################
|
|
254
|
+
def nodes_to_execute(chip, flow=None):
|
|
255
|
+
'''
|
|
256
|
+
Returns an ordered list of flowgraph nodes which will be executed.
|
|
257
|
+
This takes the from/to options into account if flow is the current flow or None.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
A list of nodes that will get executed during run() (or a specific flow).
|
|
261
|
+
|
|
262
|
+
Example:
|
|
263
|
+
>>> nodes = nodes_to_execute()
|
|
264
|
+
'''
|
|
265
|
+
if flow is None:
|
|
266
|
+
flow = chip.get('option', 'flow')
|
|
267
|
+
|
|
268
|
+
from_nodes = _get_execution_entry_nodes(chip, flow)
|
|
269
|
+
to_nodes = _get_execution_exit_nodes(chip, flow)
|
|
270
|
+
prune_nodes = chip.get('option', 'prune')
|
|
271
|
+
if from_nodes == to_nodes:
|
|
272
|
+
return list(filter(lambda node: node not in prune_nodes, from_nodes))
|
|
273
|
+
return _nodes_to_execute(chip, flow, set(from_nodes), set(to_nodes), set(prune_nodes))
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
###########################################################################
|
|
277
|
+
def _check_flowgraph(chip, flow=None):
|
|
278
|
+
'''
|
|
279
|
+
Check if flowgraph is valid.
|
|
280
|
+
|
|
281
|
+
* Checks if all edges have valid nodes
|
|
282
|
+
* Checks that there are no duplicate edges
|
|
283
|
+
* Checks if from/to is valid
|
|
284
|
+
|
|
285
|
+
Returns True if valid, False otherwise.
|
|
286
|
+
'''
|
|
287
|
+
|
|
288
|
+
if not flow:
|
|
289
|
+
flow = chip.get('option', 'flow')
|
|
290
|
+
|
|
291
|
+
error = False
|
|
292
|
+
|
|
293
|
+
nodes = set()
|
|
294
|
+
for (step, index) in _get_flowgraph_nodes(chip, flow):
|
|
295
|
+
nodes.add((step, index))
|
|
296
|
+
input_nodes = chip.get('flowgraph', flow, step, index, 'input')
|
|
297
|
+
nodes.update(input_nodes)
|
|
298
|
+
|
|
299
|
+
for node in set(input_nodes):
|
|
300
|
+
if input_nodes.count(node) > 1:
|
|
301
|
+
in_step, in_index = node
|
|
302
|
+
chip.logger.error(f'Duplicate edge from {in_step}{in_index} to '
|
|
303
|
+
f'{step}{index} in the {flow} flowgraph')
|
|
304
|
+
error = True
|
|
305
|
+
|
|
306
|
+
for step, index in nodes:
|
|
307
|
+
# For each task, check input requirements.
|
|
308
|
+
tool, task = get_tool_task(chip, step, index, flow=flow)
|
|
309
|
+
|
|
310
|
+
if not tool:
|
|
311
|
+
chip.logger.error(f'{step}{index} is missing a tool definition in the {flow} '
|
|
312
|
+
'flowgraph')
|
|
313
|
+
error = True
|
|
314
|
+
|
|
315
|
+
if not task:
|
|
316
|
+
chip.logger.error(f'{step}{index} is missing a task definition in the {flow} '
|
|
317
|
+
'flowgraph')
|
|
318
|
+
error = True
|
|
319
|
+
|
|
320
|
+
for step in chip.get('option', 'from'):
|
|
321
|
+
if step not in chip.getkeys('flowgraph', flow):
|
|
322
|
+
chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
|
|
323
|
+
error = True
|
|
324
|
+
|
|
325
|
+
for step in chip.get('option', 'to'):
|
|
326
|
+
if step not in chip.getkeys('flowgraph', flow):
|
|
327
|
+
chip.logger.error(f'{step} is not defined in the {flow} flowgraph')
|
|
328
|
+
error = True
|
|
329
|
+
|
|
330
|
+
if not _check_execution_nodes_inputs(chip, flow):
|
|
331
|
+
error = True
|
|
332
|
+
|
|
333
|
+
unreachable_steps = _unreachable_steps_to_execute(chip, flow)
|
|
334
|
+
if unreachable_steps:
|
|
335
|
+
chip.logger.error(f'These final steps in {flow} can not be reached: '
|
|
336
|
+
f'{list(unreachable_steps)}')
|
|
337
|
+
error = True
|
|
338
|
+
|
|
339
|
+
return not error
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
###########################################################################
|
|
343
|
+
def _check_flowgraph_io(chip, nodes=None):
|
|
344
|
+
'''Check if flowgraph is valid in terms of input and output files.
|
|
345
|
+
|
|
346
|
+
Returns True if valid, False otherwise.
|
|
347
|
+
'''
|
|
348
|
+
flow = chip.get('option', 'flow')
|
|
349
|
+
if not nodes:
|
|
350
|
+
nodes = nodes_to_execute(chip)
|
|
351
|
+
for (step, index) in nodes:
|
|
352
|
+
# For each task, check input requirements.
|
|
353
|
+
tool, task = get_tool_task(chip, step, index, flow=flow)
|
|
354
|
+
|
|
355
|
+
if tool == 'builtin':
|
|
356
|
+
# We can skip builtins since they don't have any particular
|
|
357
|
+
# input requirements -- they just pass through what they
|
|
358
|
+
# receive.
|
|
359
|
+
continue
|
|
360
|
+
|
|
361
|
+
# Get files we receive from input nodes.
|
|
362
|
+
in_nodes = _get_flowgraph_node_inputs(chip, flow, (step, index))
|
|
363
|
+
all_inputs = set()
|
|
364
|
+
requirements = chip.get('tool', tool, 'task', task, 'input', step=step, index=index)
|
|
365
|
+
for in_step, in_index in in_nodes:
|
|
366
|
+
if (in_step, in_index) not in nodes:
|
|
367
|
+
# If we're not running the input step, the required
|
|
368
|
+
# inputs need to already be copied into the build
|
|
369
|
+
# directory.
|
|
370
|
+
workdir = chip.getworkdir(step=in_step, index=in_index)
|
|
371
|
+
in_step_out_dir = os.path.join(workdir, 'outputs')
|
|
372
|
+
|
|
373
|
+
if not os.path.isdir(in_step_out_dir):
|
|
374
|
+
# This means this step hasn't been run, but that
|
|
375
|
+
# will be flagged by a different check. No error
|
|
376
|
+
# message here since it would be redundant.
|
|
377
|
+
inputs = []
|
|
378
|
+
continue
|
|
379
|
+
|
|
380
|
+
design = chip.get('design')
|
|
381
|
+
manifest = f'{design}.pkg.json'
|
|
382
|
+
inputs = [inp for inp in os.listdir(in_step_out_dir) if inp != manifest]
|
|
383
|
+
else:
|
|
384
|
+
inputs = _gather_outputs(chip, in_step, in_index)
|
|
385
|
+
|
|
386
|
+
for inp in inputs:
|
|
387
|
+
node_inp = input_file_node_name(inp, in_step, in_index)
|
|
388
|
+
if node_inp in requirements:
|
|
389
|
+
inp = node_inp
|
|
390
|
+
if inp in all_inputs:
|
|
391
|
+
chip.logger.error(f'Invalid flow: {step}{index} '
|
|
392
|
+
f'receives {inp} from multiple input tasks')
|
|
393
|
+
return False
|
|
394
|
+
all_inputs.add(inp)
|
|
395
|
+
|
|
396
|
+
for requirement in requirements:
|
|
397
|
+
if requirement not in all_inputs:
|
|
398
|
+
chip.logger.error(f'Invalid flow: {step}{index} will '
|
|
399
|
+
f'not receive required input {requirement}.')
|
|
400
|
+
return False
|
|
401
|
+
|
|
402
|
+
return True
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
###########################################################################
|
|
406
|
+
def _gather_outputs(chip, step, index):
|
|
407
|
+
'''Return set of filenames that are guaranteed to be in outputs
|
|
408
|
+
directory after a successful run of step/index.'''
|
|
409
|
+
|
|
410
|
+
flow = chip.get('option', 'flow')
|
|
411
|
+
task_gather = getattr(chip._get_task_module(step, index, flow=flow, error=False),
|
|
412
|
+
'_gather_outputs',
|
|
413
|
+
None)
|
|
414
|
+
if task_gather:
|
|
415
|
+
return set(task_gather(chip, step, index))
|
|
416
|
+
|
|
417
|
+
tool, task = get_tool_task(chip, step, index, flow=flow)
|
|
418
|
+
return set(chip.get('tool', tool, 'task', task, 'output', step=step, index=index))
|
|
419
|
+
|
|
420
|
+
|
|
421
|
+
def _get_flowgraph_information(chip, flow, io=True):
|
|
422
|
+
from siliconcompiler.scheduler import _setup_node
|
|
423
|
+
from siliconcompiler.tools._common import input_provides, input_file_node_name
|
|
424
|
+
|
|
425
|
+
# Save schema to avoid making permanent changes
|
|
426
|
+
org_schema = chip.schema
|
|
427
|
+
chip.schema = chip.schema.copy()
|
|
428
|
+
|
|
429
|
+
# Setup nodes
|
|
430
|
+
node_exec_order = _get_flowgraph_execution_order(chip, flow)
|
|
431
|
+
if io:
|
|
432
|
+
# try:
|
|
433
|
+
for layer_nodes in node_exec_order:
|
|
434
|
+
for step, index in layer_nodes:
|
|
435
|
+
_setup_node(chip, step, index, flow=flow)
|
|
436
|
+
# except: # noqa E722
|
|
437
|
+
# io = False
|
|
438
|
+
|
|
439
|
+
node_rank = {}
|
|
440
|
+
for rank, rank_nodes in enumerate(node_exec_order):
|
|
441
|
+
for step, index in rank_nodes:
|
|
442
|
+
node_rank[f'{step}{index}'] = rank
|
|
443
|
+
|
|
444
|
+
graph_inputs = {}
|
|
445
|
+
all_graph_inputs = set()
|
|
446
|
+
if io:
|
|
447
|
+
for step, index in _get_flowgraph_nodes(chip, flow):
|
|
448
|
+
tool, task = get_tool_task(chip, step, index, flow=flow)
|
|
449
|
+
for keypath in chip.get('tool', tool, 'task', task, 'require', step=step, index=index):
|
|
450
|
+
key = tuple(keypath.split(','))
|
|
451
|
+
if key[0] == 'input':
|
|
452
|
+
graph_inputs.setdefault((step, index), set()).add(keypath)
|
|
453
|
+
|
|
454
|
+
for inputs in graph_inputs.values():
|
|
455
|
+
all_graph_inputs.update(inputs)
|
|
456
|
+
|
|
457
|
+
exit_nodes = [f'{step}{index}' for step, index in _get_flowgraph_exit_nodes(chip, flow)]
|
|
458
|
+
|
|
459
|
+
nodes = {}
|
|
460
|
+
edges = []
|
|
461
|
+
|
|
462
|
+
def clean_label(label):
|
|
463
|
+
return label.replace("<", "").replace(">", "")
|
|
464
|
+
|
|
465
|
+
def clean_text(label):
|
|
466
|
+
return label.replace("<", r"\<").replace(">", r"\>")
|
|
467
|
+
|
|
468
|
+
all_nodes = [(step, index) for step, index in sorted(_get_flowgraph_nodes(chip, flow))
|
|
469
|
+
if chip.get('record', 'status', step=step, index=index) != NodeStatus.SKIPPED]
|
|
470
|
+
for step, index in all_nodes:
|
|
471
|
+
tool, task = get_tool_task(chip, step, index, flow=flow)
|
|
472
|
+
|
|
473
|
+
if io:
|
|
474
|
+
inputs = chip.get('tool', tool, 'task', task, 'input', step=step, index=index)
|
|
475
|
+
outputs = chip.get('tool', tool, 'task', task, 'output', step=step, index=index)
|
|
476
|
+
if chip.get('record', 'status', step=step, index=index) == NodeStatus.SKIPPED:
|
|
477
|
+
continue
|
|
478
|
+
else:
|
|
479
|
+
inputs = []
|
|
480
|
+
outputs = []
|
|
481
|
+
|
|
482
|
+
node = f'{step}{index}'
|
|
483
|
+
if io and (step, index) in graph_inputs:
|
|
484
|
+
inputs.extend(graph_inputs[(step, index)])
|
|
485
|
+
|
|
486
|
+
nodes[node] = {
|
|
487
|
+
"node": (step, index),
|
|
488
|
+
"file_inputs": inputs,
|
|
489
|
+
"inputs": {clean_text(f): f'input-{clean_label(f)}' for f in sorted(inputs)},
|
|
490
|
+
"outputs": {clean_text(f): f'output-{clean_label(f)}' for f in sorted(outputs)},
|
|
491
|
+
"task": f'{tool}/{task}' if tool != 'builtin' else task,
|
|
492
|
+
"is_input": node_rank[node] == 0,
|
|
493
|
+
"rank": node_rank[node]
|
|
494
|
+
}
|
|
495
|
+
nodes[node]["width"] = max(len(nodes[node]["inputs"]), len(nodes[node]["outputs"]))
|
|
496
|
+
|
|
497
|
+
if tool is None or task is None:
|
|
498
|
+
nodes[node]["task"] = None
|
|
499
|
+
|
|
500
|
+
rank_diff = {}
|
|
501
|
+
for in_step, in_index in _get_flowgraph_node_inputs(chip, flow, (step, index)):
|
|
502
|
+
rank_diff[f'{in_step}{in_index}'] = node_rank[node] - node_rank[f'{in_step}{in_index}']
|
|
503
|
+
nodes[node]["rank_diff"] = rank_diff
|
|
504
|
+
|
|
505
|
+
for step, index in all_nodes:
|
|
506
|
+
node = f'{step}{index}'
|
|
507
|
+
if io:
|
|
508
|
+
# get inputs
|
|
509
|
+
edge_stats = {}
|
|
510
|
+
for infile, in_nodes in input_provides(chip, step, index, flow=flow).items():
|
|
511
|
+
outfile = infile
|
|
512
|
+
for in_step, in_index in in_nodes:
|
|
513
|
+
infile = outfile
|
|
514
|
+
if infile not in nodes[node]["file_inputs"]:
|
|
515
|
+
infile = input_file_node_name(infile, in_step, in_index)
|
|
516
|
+
if infile not in nodes[node]["file_inputs"]:
|
|
517
|
+
continue
|
|
518
|
+
in_node_name = f"{in_step}{in_index}"
|
|
519
|
+
outlabel = f"{in_node_name}:output-{clean_label(outfile)}"
|
|
520
|
+
inlabel = f"{step}{index}:input-{clean_label(infile)}"
|
|
521
|
+
|
|
522
|
+
if in_node_name not in edge_stats:
|
|
523
|
+
edge_stats[in_node_name] = {
|
|
524
|
+
"count": 0,
|
|
525
|
+
"pairs": [],
|
|
526
|
+
"weight": min(nodes[node]["width"], nodes[in_node_name]["width"])
|
|
527
|
+
}
|
|
528
|
+
edge_stats[in_node_name]["count"] += 1
|
|
529
|
+
edge_stats[in_node_name]["pairs"].append((outlabel, inlabel))
|
|
530
|
+
|
|
531
|
+
# assign edge weights
|
|
532
|
+
|
|
533
|
+
# scale multiple weights
|
|
534
|
+
for edge_data in edge_stats.values():
|
|
535
|
+
edge_data["weight"] = int(
|
|
536
|
+
math.floor(max(1, edge_data["weight"] / edge_data["count"])))
|
|
537
|
+
|
|
538
|
+
# lower exit nodes weights
|
|
539
|
+
if node in exit_nodes:
|
|
540
|
+
for edge_data in edge_stats.values():
|
|
541
|
+
edge_data["weight"] = 1
|
|
542
|
+
else:
|
|
543
|
+
for edge_data in edge_stats.values():
|
|
544
|
+
edge_data["weight"] *= 2
|
|
545
|
+
|
|
546
|
+
# adjust for rank differences, lower weight if rankdiff is greater than 1
|
|
547
|
+
for in_node, edge_data in edge_stats.items():
|
|
548
|
+
if nodes[node]["rank_diff"][in_node] > 1:
|
|
549
|
+
edge_data["weight"] = 1
|
|
550
|
+
|
|
551
|
+
# create edges
|
|
552
|
+
for edge_data in edge_stats.values():
|
|
553
|
+
for outlabel, inlabel in edge_data["pairs"]:
|
|
554
|
+
edges.append([outlabel, inlabel, edge_data["weight"]])
|
|
555
|
+
|
|
556
|
+
if (step, index) in graph_inputs:
|
|
557
|
+
for key in graph_inputs[(step, index)]:
|
|
558
|
+
inlabel = f"{step}{index}:input-{clean_label(key)}"
|
|
559
|
+
edges.append((key, inlabel, 1))
|
|
560
|
+
else:
|
|
561
|
+
all_inputs = []
|
|
562
|
+
for in_step, in_index in chip.get('flowgraph', flow, step, index, 'input'):
|
|
563
|
+
all_inputs.append(f'{in_step}{in_index}')
|
|
564
|
+
for item in all_inputs:
|
|
565
|
+
edges.append((item, node, 1 if node in exit_nodes else 2))
|
|
566
|
+
|
|
567
|
+
# Restore schema
|
|
568
|
+
chip.schema = org_schema
|
|
569
|
+
|
|
570
|
+
return all_graph_inputs, nodes, edges, io
|
|
File without changes
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
from siliconcompiler.tools.surelog import parse as surelog_parse
|
|
2
|
+
from siliconcompiler.tools.chisel import convert as chisel_convert
|
|
3
|
+
from siliconcompiler.tools.bambu import convert as bambu_convert
|
|
4
|
+
from siliconcompiler.tools.bluespec import convert as bluespec_convert
|
|
5
|
+
from siliconcompiler.tools.ghdl import convert as ghdl_convert
|
|
6
|
+
from siliconcompiler.tools.sv2v import convert as sv2v_convert
|
|
7
|
+
|
|
8
|
+
from siliconcompiler.tools.builtin import concatenate
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _make_docs(chip):
|
|
12
|
+
from siliconcompiler.targets import freepdk45_demo
|
|
13
|
+
chip.set('input', 'rtl', 'vhdl', 'test')
|
|
14
|
+
chip.set('input', 'rtl', 'verilog', 'test')
|
|
15
|
+
chip.set('input', 'rtl', 'systemverilog', 'test')
|
|
16
|
+
chip.set('input', 'hll', 'c', 'test')
|
|
17
|
+
chip.set('input', 'hll', 'bsv', 'test')
|
|
18
|
+
chip.set('input', 'hll', 'scala', 'test')
|
|
19
|
+
|
|
20
|
+
chip.load_target(freepdk45_demo)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def __get_frontends(allow_system_verilog):
|
|
24
|
+
systemverilog_frontend = [
|
|
25
|
+
('import', surelog_parse)
|
|
26
|
+
]
|
|
27
|
+
if not allow_system_verilog:
|
|
28
|
+
systemverilog_frontend.append(('convert', sv2v_convert))
|
|
29
|
+
|
|
30
|
+
return {
|
|
31
|
+
"verilog": systemverilog_frontend,
|
|
32
|
+
"chisel": [('import', chisel_convert)],
|
|
33
|
+
"c": [('import', bambu_convert)],
|
|
34
|
+
"bluespec": [('import', bluespec_convert)],
|
|
35
|
+
"vhdl": [('import', ghdl_convert)]
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def setup_multiple_frontends(chip, flow, allow_system_verilog=False):
|
|
40
|
+
'''
|
|
41
|
+
Sets of multiple frontends if different frontends are required.
|
|
42
|
+
|
|
43
|
+
Returns name of final step from the setup.
|
|
44
|
+
'''
|
|
45
|
+
|
|
46
|
+
concat_nodes = []
|
|
47
|
+
flowname = flow.design
|
|
48
|
+
for frontend, pipe in __get_frontends(allow_system_verilog).items():
|
|
49
|
+
prev_step = None
|
|
50
|
+
for step, task in pipe:
|
|
51
|
+
step_name = f'{step}_{frontend}'
|
|
52
|
+
|
|
53
|
+
flow.node(flowname, step_name, task)
|
|
54
|
+
if prev_step:
|
|
55
|
+
flow.edge(flowname, prev_step, step_name)
|
|
56
|
+
|
|
57
|
+
prev_step = step_name
|
|
58
|
+
|
|
59
|
+
if prev_step:
|
|
60
|
+
concat_nodes.append(prev_step)
|
|
61
|
+
|
|
62
|
+
final_node = 'combine'
|
|
63
|
+
flow.node(flowname, final_node, concatenate)
|
|
64
|
+
for node in concat_nodes:
|
|
65
|
+
flow.edge(flowname, node, final_node)
|
|
66
|
+
|
|
67
|
+
return final_node
|