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,253 @@
|
|
|
1
|
+
import docker
|
|
2
|
+
import os
|
|
3
|
+
from siliconcompiler.package import get_cache_path
|
|
4
|
+
from siliconcompiler.package import _path as sc_path
|
|
5
|
+
from siliconcompiler.utils import default_email_credentials_file
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def get_image(chip, step, index):
|
|
11
|
+
from siliconcompiler import __version__
|
|
12
|
+
|
|
13
|
+
queue = chip.get('option', 'scheduler', 'queue', step=step, index=index)
|
|
14
|
+
if queue:
|
|
15
|
+
return queue
|
|
16
|
+
|
|
17
|
+
return os.getenv(
|
|
18
|
+
'SC_DOCKER_IMAGE',
|
|
19
|
+
f'ghcr.io/siliconcompiler/sc_runner:v{__version__}')
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_volumes_directories(chip, cache_dir, workdir, step, index):
|
|
23
|
+
all_dirs = set()
|
|
24
|
+
# Collect files
|
|
25
|
+
for key in chip.allkeys():
|
|
26
|
+
sc_type = chip.get(*key, field='type')
|
|
27
|
+
|
|
28
|
+
if 'file' in sc_type or 'dir' in sc_type:
|
|
29
|
+
cstep = step
|
|
30
|
+
cindex = index
|
|
31
|
+
|
|
32
|
+
if 'never' in chip.get(*key, field='pernode'):
|
|
33
|
+
cstep = None
|
|
34
|
+
cindex = None
|
|
35
|
+
|
|
36
|
+
files = chip.find_files(*key, step=cstep, index=cindex, missing_ok=True)
|
|
37
|
+
if files:
|
|
38
|
+
if not isinstance(files, list):
|
|
39
|
+
files = [files]
|
|
40
|
+
for path in files:
|
|
41
|
+
if path is None:
|
|
42
|
+
continue
|
|
43
|
+
if 'file' in sc_type:
|
|
44
|
+
all_dirs.add(os.path.dirname(path))
|
|
45
|
+
else:
|
|
46
|
+
all_dirs.add(path)
|
|
47
|
+
|
|
48
|
+
# Collect caches
|
|
49
|
+
for package in chip.getkeys('package', 'source'):
|
|
50
|
+
all_dirs.add(sc_path(chip, package, None))
|
|
51
|
+
|
|
52
|
+
all_dirs = [
|
|
53
|
+
Path(cache_dir),
|
|
54
|
+
Path(workdir),
|
|
55
|
+
Path(chip.scroot),
|
|
56
|
+
*[Path(path) for path in all_dirs]]
|
|
57
|
+
|
|
58
|
+
pruned_dirs = all_dirs.copy()
|
|
59
|
+
for base_path in all_dirs:
|
|
60
|
+
if base_path not in pruned_dirs:
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
new_pruned_dirs = [base_path]
|
|
64
|
+
for check_path in pruned_dirs:
|
|
65
|
+
if base_path == check_path:
|
|
66
|
+
continue
|
|
67
|
+
|
|
68
|
+
if base_path not in check_path.parents:
|
|
69
|
+
new_pruned_dirs.append(check_path)
|
|
70
|
+
pruned_dirs = new_pruned_dirs
|
|
71
|
+
|
|
72
|
+
pruned_dirs = set(pruned_dirs)
|
|
73
|
+
|
|
74
|
+
builddir = chip.find_files('option', 'builddir')
|
|
75
|
+
|
|
76
|
+
rw_volumes = set()
|
|
77
|
+
|
|
78
|
+
for path in pruned_dirs:
|
|
79
|
+
for rw_allow in (Path(builddir), Path(workdir), Path(cache_dir)):
|
|
80
|
+
if path == rw_allow or path in rw_allow.parents:
|
|
81
|
+
rw_volumes.add(path)
|
|
82
|
+
|
|
83
|
+
ro_volumes = pruned_dirs.difference(rw_volumes)
|
|
84
|
+
|
|
85
|
+
return rw_volumes, ro_volumes
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def init(chip):
|
|
89
|
+
if sys.platform == 'win32':
|
|
90
|
+
# this avoids the issue of different file system types
|
|
91
|
+
chip.logger.error('Setting copy field to true for docker run on Windows')
|
|
92
|
+
for key in chip.allkeys():
|
|
93
|
+
if key[0] == 'history':
|
|
94
|
+
continue
|
|
95
|
+
sc_type = chip.get(*key, field='type')
|
|
96
|
+
if 'dir' in sc_type or 'file' in sc_type:
|
|
97
|
+
chip.set(*key, True, field='copy')
|
|
98
|
+
chip.collect()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def run(chip, step, index, replay):
|
|
102
|
+
# Import here to avoid circular import
|
|
103
|
+
from siliconcompiler.scheduler import _haltstep
|
|
104
|
+
|
|
105
|
+
try:
|
|
106
|
+
client = docker.from_env()
|
|
107
|
+
client.version()
|
|
108
|
+
except (docker.errors.DockerException, docker.errors.APIError) as e:
|
|
109
|
+
chip.logger.error(f'Unable to connect to docker: {e}')
|
|
110
|
+
_haltstep(chip, chip.get('option', 'flow'), step, index)
|
|
111
|
+
|
|
112
|
+
is_windows = sys.platform == 'win32'
|
|
113
|
+
|
|
114
|
+
workdir = chip.getworkdir()
|
|
115
|
+
start_cwd = os.getcwd()
|
|
116
|
+
|
|
117
|
+
# Remove handlers from logger
|
|
118
|
+
chip.logger.handlers.clear()
|
|
119
|
+
|
|
120
|
+
# Reinit logger
|
|
121
|
+
chip._init_logger(step=step, index=index, in_run=True)
|
|
122
|
+
|
|
123
|
+
# Change working directory since the run may delete this folder
|
|
124
|
+
os.chdir(workdir)
|
|
125
|
+
|
|
126
|
+
image_name = get_image(chip, step, index)
|
|
127
|
+
|
|
128
|
+
# Pull image if needed
|
|
129
|
+
try:
|
|
130
|
+
image = client.images.get(image_name)
|
|
131
|
+
except docker.errors.ImageNotFound:
|
|
132
|
+
# Needs a lock to avoid downloading a bunch in parallel
|
|
133
|
+
image_repo, image_tag = image_name.split(':')
|
|
134
|
+
chip.logger.info(f'Pulling docker image {image_name}')
|
|
135
|
+
try:
|
|
136
|
+
image = client.images.pull(image_repo, tag=image_tag)
|
|
137
|
+
except docker.errors.APIError as e:
|
|
138
|
+
chip.logger.error(f'Unable to pull image: {e}')
|
|
139
|
+
image_src = image_repo.split('/')[0]
|
|
140
|
+
chip.logger.error(f" if you are logged into {image_src} with expired credentials, "
|
|
141
|
+
f"please use 'docker logout {image_src}'")
|
|
142
|
+
_haltstep(chip, chip.get('option', 'flow'), step, index)
|
|
143
|
+
|
|
144
|
+
email_file = default_email_credentials_file()
|
|
145
|
+
if is_windows:
|
|
146
|
+
# Hack to get around manifest merging
|
|
147
|
+
chip.set('option', 'cachedir', None)
|
|
148
|
+
cache_dir = '/sc_cache'
|
|
149
|
+
cwd = '/sc_docker'
|
|
150
|
+
builddir = f'{cwd}/build'
|
|
151
|
+
|
|
152
|
+
local_cfg = os.path.join(start_cwd, 'sc_docker.json')
|
|
153
|
+
job = chip.get('option', 'jobname')
|
|
154
|
+
cfg = f'{builddir}/{chip.design}/{job}/{step}/{index}/sc_docker.json'
|
|
155
|
+
|
|
156
|
+
user = None
|
|
157
|
+
|
|
158
|
+
volumes = [
|
|
159
|
+
f"{chip.cwd}:{cwd}:rw",
|
|
160
|
+
f"{get_cache_path(chip)}:{cache_dir}:rw"
|
|
161
|
+
]
|
|
162
|
+
chip.logger.debug(f'Volumes: {volumes}')
|
|
163
|
+
|
|
164
|
+
env = {}
|
|
165
|
+
|
|
166
|
+
if os.path.exists(email_file):
|
|
167
|
+
env["HOME"] = "/sc_home"
|
|
168
|
+
|
|
169
|
+
volumes.append(f'{os.path.dirname(email_file)}:/sc_home/.sc:ro')
|
|
170
|
+
else:
|
|
171
|
+
cache_dir = get_cache_path(chip)
|
|
172
|
+
cwd = chip.cwd
|
|
173
|
+
builddir = chip.find_files('option', 'builddir')
|
|
174
|
+
|
|
175
|
+
local_cfg = os.path.abspath('sc_docker.json')
|
|
176
|
+
cfg = local_cfg
|
|
177
|
+
|
|
178
|
+
user = os.getuid()
|
|
179
|
+
|
|
180
|
+
rw_volumes, ro_volumes = get_volumes_directories(chip, cache_dir, workdir, step, index)
|
|
181
|
+
volumes = [
|
|
182
|
+
*[
|
|
183
|
+
f'{path}:{path}:rw' for path in rw_volumes
|
|
184
|
+
],
|
|
185
|
+
*[
|
|
186
|
+
f'{path}:{path}:ro' for path in ro_volumes
|
|
187
|
+
]
|
|
188
|
+
]
|
|
189
|
+
chip.logger.debug(f'Read write volumes: {rw_volumes}')
|
|
190
|
+
chip.logger.debug(f'Read only volumes: {ro_volumes}')
|
|
191
|
+
|
|
192
|
+
env = {}
|
|
193
|
+
if os.path.exists(email_file):
|
|
194
|
+
env["HOME"] = "/sc_home"
|
|
195
|
+
|
|
196
|
+
volumes.append(f'{os.path.dirname(email_file)}:/sc_home/.sc:ro')
|
|
197
|
+
|
|
198
|
+
container = None
|
|
199
|
+
try:
|
|
200
|
+
container = client.containers.run(
|
|
201
|
+
image.id,
|
|
202
|
+
volumes=volumes,
|
|
203
|
+
labels=[
|
|
204
|
+
"siliconcompiler",
|
|
205
|
+
f"sc_node:{chip.design}:{step}{index}"
|
|
206
|
+
],
|
|
207
|
+
user=user,
|
|
208
|
+
detach=True,
|
|
209
|
+
tty=True,
|
|
210
|
+
auto_remove=True,
|
|
211
|
+
environment=env)
|
|
212
|
+
|
|
213
|
+
# Write manifest to make it available to the docker runner
|
|
214
|
+
chip.write_manifest(local_cfg)
|
|
215
|
+
|
|
216
|
+
cachemap = []
|
|
217
|
+
for package in chip.getkeys('package', 'source'):
|
|
218
|
+
cachemap.append(f'{package}:{sc_path(chip, package, None)}')
|
|
219
|
+
|
|
220
|
+
chip.logger.info(f'Running in docker container: {container.name} ({container.short_id})')
|
|
221
|
+
args = [
|
|
222
|
+
'-cfg', cfg,
|
|
223
|
+
'-cwd', cwd,
|
|
224
|
+
'-builddir', builddir,
|
|
225
|
+
'-cachedir', cache_dir,
|
|
226
|
+
'-step', step,
|
|
227
|
+
'-index', index,
|
|
228
|
+
'-unset_scheduler'
|
|
229
|
+
]
|
|
230
|
+
if not is_windows and cachemap:
|
|
231
|
+
args.append('-cachemap')
|
|
232
|
+
args.append(' '.join(cachemap))
|
|
233
|
+
cmd = f'python3 -m siliconcompiler.scheduler.run_node {" ".join(args)}'
|
|
234
|
+
exec_handle = client.api.exec_create(container.name, cmd)
|
|
235
|
+
stream = client.api.exec_start(exec_handle, stream=True)
|
|
236
|
+
|
|
237
|
+
# Print the log
|
|
238
|
+
for chunk in stream:
|
|
239
|
+
for line in chunk.decode().splitlines():
|
|
240
|
+
print(line)
|
|
241
|
+
|
|
242
|
+
if client.api.exec_inspect(exec_handle['Id']).get('ExitCode') != 0:
|
|
243
|
+
_haltstep(chip, chip.get('option', 'flow'), step, index, log=False)
|
|
244
|
+
finally:
|
|
245
|
+
# Ensure we clean up containers
|
|
246
|
+
if container:
|
|
247
|
+
try:
|
|
248
|
+
container.stop()
|
|
249
|
+
except docker.errors.APIError:
|
|
250
|
+
chip.logger.error('Failed to stop docker container')
|
|
251
|
+
|
|
252
|
+
# Restore working directory
|
|
253
|
+
os.chdir(start_cwd)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import os
|
|
5
|
+
import sys
|
|
6
|
+
import tarfile
|
|
7
|
+
from siliconcompiler import Chip, Schema
|
|
8
|
+
from siliconcompiler.package import _path as sc_path
|
|
9
|
+
from siliconcompiler.scheduler import _runtask, _executenode
|
|
10
|
+
from siliconcompiler import __version__
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
##########################
|
|
14
|
+
def main():
|
|
15
|
+
schema = Schema()
|
|
16
|
+
|
|
17
|
+
# Can't use chip.cmdline because we don't want a bunch of extra logger information
|
|
18
|
+
parser = argparse.ArgumentParser(prog='run_node',
|
|
19
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
20
|
+
description='Script to run a single node in an SC flowgraph')
|
|
21
|
+
|
|
22
|
+
parser.add_argument('-version',
|
|
23
|
+
action='version',
|
|
24
|
+
version=__version__)
|
|
25
|
+
parser.add_argument('-cfg',
|
|
26
|
+
required=True,
|
|
27
|
+
metavar='<file>',
|
|
28
|
+
help=schema.get('option', 'cfg',
|
|
29
|
+
field='shorthelp'))
|
|
30
|
+
parser.add_argument('-cwd',
|
|
31
|
+
required=True,
|
|
32
|
+
metavar='<directory>',
|
|
33
|
+
help='Run current working directory')
|
|
34
|
+
parser.add_argument('-builddir',
|
|
35
|
+
metavar='<directory>',
|
|
36
|
+
required=True,
|
|
37
|
+
help=schema.get('option', 'builddir',
|
|
38
|
+
field='shorthelp'))
|
|
39
|
+
parser.add_argument('-cachedir',
|
|
40
|
+
metavar='<directory>',
|
|
41
|
+
required=True,
|
|
42
|
+
help=schema.get('option', 'cachedir',
|
|
43
|
+
field='shorthelp'))
|
|
44
|
+
parser.add_argument('-cachemap',
|
|
45
|
+
metavar='<package>:<directory>',
|
|
46
|
+
nargs='+',
|
|
47
|
+
help='Map of caches to prepopulate runner with')
|
|
48
|
+
parser.add_argument('-step',
|
|
49
|
+
required=True,
|
|
50
|
+
metavar='<step>',
|
|
51
|
+
help=schema.get('arg', 'step',
|
|
52
|
+
field='shorthelp'))
|
|
53
|
+
parser.add_argument('-index',
|
|
54
|
+
required=True,
|
|
55
|
+
metavar='<index>',
|
|
56
|
+
help=schema.get('arg', 'index',
|
|
57
|
+
field='shorthelp'))
|
|
58
|
+
parser.add_argument('-remoteid',
|
|
59
|
+
metavar='<id>',
|
|
60
|
+
help=schema.get('record', 'remoteid',
|
|
61
|
+
field='shorthelp'))
|
|
62
|
+
parser.add_argument('-archive',
|
|
63
|
+
metavar='<file>',
|
|
64
|
+
help='Generate archive')
|
|
65
|
+
parser.add_argument('-include',
|
|
66
|
+
metavar='<path>',
|
|
67
|
+
nargs='+',
|
|
68
|
+
help='Files to include in archive')
|
|
69
|
+
parser.add_argument('-unset_scheduler',
|
|
70
|
+
action='store_true',
|
|
71
|
+
help='Unset scheduler to ensure local run')
|
|
72
|
+
args = parser.parse_args()
|
|
73
|
+
|
|
74
|
+
# Change to working directory to allow rel path to be build dir
|
|
75
|
+
# this avoids needing to deal with the job hash on the client
|
|
76
|
+
# side
|
|
77
|
+
os.chdir(args.cwd)
|
|
78
|
+
|
|
79
|
+
# Create the Chip object.
|
|
80
|
+
chip = Chip('<design>')
|
|
81
|
+
chip.read_manifest(args.cfg)
|
|
82
|
+
|
|
83
|
+
# setup work directory
|
|
84
|
+
chip.set('arg', 'step', args.step)
|
|
85
|
+
chip.set('arg', 'index', args.index)
|
|
86
|
+
chip.set('option', 'builddir', args.builddir)
|
|
87
|
+
chip.set('option', 'cachedir', args.cachedir)
|
|
88
|
+
|
|
89
|
+
if args.remoteid:
|
|
90
|
+
chip.set('record', 'remoteid', args.remoteid)
|
|
91
|
+
|
|
92
|
+
if args.unset_scheduler:
|
|
93
|
+
for vals, step, index in chip.schema._getvals('option', 'scheduler', 'name'):
|
|
94
|
+
chip.unset('option', 'scheduler', 'name', step=step, index=index)
|
|
95
|
+
|
|
96
|
+
# Init logger to ensure consistent view
|
|
97
|
+
chip._init_logger(step=chip.get('arg', 'step'),
|
|
98
|
+
index=chip.get('arg', 'index'),
|
|
99
|
+
in_run=True)
|
|
100
|
+
|
|
101
|
+
if args.cachemap:
|
|
102
|
+
for cachepair in args.cachemap:
|
|
103
|
+
package, path = cachepair.split(':')
|
|
104
|
+
chip._packages[package] = path
|
|
105
|
+
|
|
106
|
+
# Populate cache without downloading
|
|
107
|
+
for package in chip.getkeys('package', 'source'):
|
|
108
|
+
sc_path(chip, package, None)
|
|
109
|
+
|
|
110
|
+
# Run the task.
|
|
111
|
+
error = True
|
|
112
|
+
try:
|
|
113
|
+
_runtask(chip,
|
|
114
|
+
chip.get('option', 'flow'),
|
|
115
|
+
chip.get('arg', 'step'),
|
|
116
|
+
chip.get('arg', 'index'),
|
|
117
|
+
_executenode)
|
|
118
|
+
error = False
|
|
119
|
+
|
|
120
|
+
finally:
|
|
121
|
+
if args.archive:
|
|
122
|
+
# Archive the results.
|
|
123
|
+
with tarfile.open(args.archive,
|
|
124
|
+
mode='w:gz') as tf:
|
|
125
|
+
chip._archive_node(tf,
|
|
126
|
+
step=args.step,
|
|
127
|
+
index=args.index,
|
|
128
|
+
include=args.include)
|
|
129
|
+
|
|
130
|
+
# Return success/fail flag, in case the caller is interested.
|
|
131
|
+
if error:
|
|
132
|
+
return 1
|
|
133
|
+
return 0
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
##########################
|
|
137
|
+
if __name__ == "__main__":
|
|
138
|
+
sys.exit(main())
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
from siliconcompiler.utils import default_email_credentials_file, get_file_template
|
|
2
|
+
import smtplib
|
|
3
|
+
from email.mime.multipart import MIMEMultipart
|
|
4
|
+
from email.mime.text import MIMEText
|
|
5
|
+
from email.mime.application import MIMEApplication
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from siliconcompiler import sc_open
|
|
9
|
+
from siliconcompiler.schema import Schema
|
|
10
|
+
from siliconcompiler.report import utils as report_utils
|
|
11
|
+
import fastjsonschema
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
from siliconcompiler.flowgraph import get_executed_nodes
|
|
14
|
+
import uuid
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# Compile validation code for API request bodies.
|
|
18
|
+
api_dir = Path(__file__).parent / 'validation'
|
|
19
|
+
|
|
20
|
+
# 'remote_run': Run a stage of a job using the server's cluster settings.
|
|
21
|
+
with open(api_dir / 'email_credentials.json') as schema:
|
|
22
|
+
validate_creds = fastjsonschema.compile(json.loads(schema.read()))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def __load_config(chip):
|
|
26
|
+
path = default_email_credentials_file()
|
|
27
|
+
if not os.path.exists(path):
|
|
28
|
+
chip.logger.warn(f'Email credentials are not available: {path}')
|
|
29
|
+
return {}
|
|
30
|
+
|
|
31
|
+
with open(path) as f:
|
|
32
|
+
creds = json.load(f)
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
return validate_creds(creds)
|
|
36
|
+
except fastjsonschema.JsonSchemaException as e:
|
|
37
|
+
chip.logger.error(f'Email credentials failed to validate: {e}')
|
|
38
|
+
return {}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def send(chip, msg_type, step, index):
|
|
42
|
+
chip_step, chip_index = step, index
|
|
43
|
+
if step is None:
|
|
44
|
+
chip_step = Schema.GLOBAL_KEY
|
|
45
|
+
if index is None:
|
|
46
|
+
chip_index = Schema.GLOBAL_KEY
|
|
47
|
+
to = chip.get('option', 'scheduler', 'msgcontact', step=chip_step, index=chip_index)
|
|
48
|
+
event = chip.get('option', 'scheduler', 'msgevent', step=chip_step, index=chip_index)
|
|
49
|
+
|
|
50
|
+
if not to or not event:
|
|
51
|
+
# nothing to do
|
|
52
|
+
return
|
|
53
|
+
|
|
54
|
+
if 'all' not in event and msg_type not in event:
|
|
55
|
+
# nothing to do
|
|
56
|
+
return
|
|
57
|
+
|
|
58
|
+
cred = __load_config(chip)
|
|
59
|
+
|
|
60
|
+
if not cred:
|
|
61
|
+
return
|
|
62
|
+
|
|
63
|
+
jobname = chip.get("option", "jobname")
|
|
64
|
+
flow = chip.get("option", "flow")
|
|
65
|
+
|
|
66
|
+
msg = MIMEMultipart()
|
|
67
|
+
|
|
68
|
+
if step and index:
|
|
69
|
+
subject = f'SiliconCompiler : {chip.design} | {jobname} | {step}{index} | {msg_type}'
|
|
70
|
+
else:
|
|
71
|
+
subject = f'SiliconCompiler : {chip.design} | {jobname} | {msg_type}'
|
|
72
|
+
|
|
73
|
+
# Setup email header
|
|
74
|
+
msg['Subject'] = subject
|
|
75
|
+
|
|
76
|
+
if "from" in cred:
|
|
77
|
+
msg['From'] = cred["from"]
|
|
78
|
+
else:
|
|
79
|
+
msg['From'] = to[0]
|
|
80
|
+
msg['To'] = ", ".join(to)
|
|
81
|
+
msg['X-Entity-Ref-ID'] = uuid.uuid4().hex # keep emails from getting grouped
|
|
82
|
+
|
|
83
|
+
if msg_type == "summary":
|
|
84
|
+
layout_img = report_utils._find_summary_image(chip)
|
|
85
|
+
if layout_img and os.path.isfile(layout_img):
|
|
86
|
+
with open(layout_img, 'rb') as img_file:
|
|
87
|
+
img_attach = MIMEApplication(img_file.read())
|
|
88
|
+
img_attach.add_header('Content-Disposition',
|
|
89
|
+
'attachment',
|
|
90
|
+
filename=os.path.basename(layout_img))
|
|
91
|
+
msg.attach(img_attach)
|
|
92
|
+
|
|
93
|
+
nodes_to_execute = get_executed_nodes(chip, flow)
|
|
94
|
+
nodes, errors, metrics, metrics_unit, metrics_to_show, _ = \
|
|
95
|
+
report_utils._collect_data(chip, flow=flow, flowgraph_nodes=nodes_to_execute)
|
|
96
|
+
|
|
97
|
+
text_msg = get_file_template('email/summary.j2').render(
|
|
98
|
+
design=chip.design,
|
|
99
|
+
nodes=nodes,
|
|
100
|
+
errors=errors,
|
|
101
|
+
metrics=metrics,
|
|
102
|
+
metrics_unit=metrics_unit,
|
|
103
|
+
metric_keys=metrics_to_show)
|
|
104
|
+
else:
|
|
105
|
+
# Attach logs
|
|
106
|
+
for log in (f'sc_{step}{index}.log', f'{step}.log'):
|
|
107
|
+
log_file = f'{chip.getworkdir(step=step, index=index)}/{log}'
|
|
108
|
+
if os.path.exists(log_file):
|
|
109
|
+
with sc_open(log_file) as f:
|
|
110
|
+
log_attach = MIMEApplication(f.read())
|
|
111
|
+
log_name, _ = os.path.splitext(log)
|
|
112
|
+
# Make attachment a txt file to avoid issues with tools not loading .log
|
|
113
|
+
log_attach.add_header('Content-Disposition',
|
|
114
|
+
'attachment',
|
|
115
|
+
filename=f'{log_name}.txt')
|
|
116
|
+
msg.attach(log_attach)
|
|
117
|
+
|
|
118
|
+
records = {}
|
|
119
|
+
for record in chip.getkeys('record'):
|
|
120
|
+
value = None
|
|
121
|
+
if chip.get('record', record, field='pernode') == 'never':
|
|
122
|
+
value = chip.get('record', record)
|
|
123
|
+
else:
|
|
124
|
+
value = chip.get('record', record, step=step, index=index)
|
|
125
|
+
|
|
126
|
+
if value is not None:
|
|
127
|
+
records[record] = value
|
|
128
|
+
|
|
129
|
+
nodes, errors, metrics, metrics_unit, metrics_to_show, _ = \
|
|
130
|
+
report_utils._collect_data(chip, flow=flow, flowgraph_nodes=[(step, index)])
|
|
131
|
+
|
|
132
|
+
status = chip.get('record', 'status', step=step, index=index)
|
|
133
|
+
|
|
134
|
+
text_msg = get_file_template('email/general.j2').render(
|
|
135
|
+
design=chip.design,
|
|
136
|
+
job=jobname,
|
|
137
|
+
step=step,
|
|
138
|
+
index=index,
|
|
139
|
+
status=status,
|
|
140
|
+
records=records,
|
|
141
|
+
nodes=nodes,
|
|
142
|
+
errors=errors,
|
|
143
|
+
metrics=metrics,
|
|
144
|
+
metrics_unit=metrics_unit,
|
|
145
|
+
metric_keys=metrics_to_show)
|
|
146
|
+
|
|
147
|
+
body = MIMEText(text_msg, 'html')
|
|
148
|
+
msg.attach(body)
|
|
149
|
+
|
|
150
|
+
if cred['ssl']:
|
|
151
|
+
smtp_use = smtplib.SMTP_SSL
|
|
152
|
+
else:
|
|
153
|
+
smtp_use = smtplib.SMTP
|
|
154
|
+
|
|
155
|
+
with smtp_use(cred["server"], cred["port"]) as smtp_server:
|
|
156
|
+
do_send = False
|
|
157
|
+
try:
|
|
158
|
+
smtp_server.login(cred["username"], cred["password"])
|
|
159
|
+
do_send = True
|
|
160
|
+
except smtplib.SMTPAuthenticationError as e:
|
|
161
|
+
chip.logger.error(f'Unable to authenticate to email server: {e}')
|
|
162
|
+
except Exception as e:
|
|
163
|
+
chip.logger.error(f'An error occurred during login to email server: {e}')
|
|
164
|
+
|
|
165
|
+
if do_send:
|
|
166
|
+
try:
|
|
167
|
+
smtp_server.sendmail(msg['From'], to, msg.as_string())
|
|
168
|
+
except Exception as e:
|
|
169
|
+
chip.logger.error(f'An error occurred while sending email: {e}')
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
if __name__ == "__main__":
|
|
173
|
+
from siliconcompiler import Chip
|
|
174
|
+
chip = Chip('test')
|
|
175
|
+
chip.load_target("freepdk45_demo")
|
|
176
|
+
chip.set('option', 'scheduler', 'msgevent', 'ALL')
|
|
177
|
+
# chip.set('option', 'scheduler', 'msgcontact', 'fillin')
|
|
178
|
+
send(chip, "BEGIN", "import", "0")
|