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.
Files changed (251) hide show
  1. siliconcompiler/__init__.py +24 -0
  2. siliconcompiler/__main__.py +12 -0
  3. siliconcompiler/_common.py +49 -0
  4. siliconcompiler/_metadata.py +36 -0
  5. siliconcompiler/apps/__init__.py +0 -0
  6. siliconcompiler/apps/_common.py +76 -0
  7. siliconcompiler/apps/sc.py +92 -0
  8. siliconcompiler/apps/sc_dashboard.py +94 -0
  9. siliconcompiler/apps/sc_issue.py +178 -0
  10. siliconcompiler/apps/sc_remote.py +199 -0
  11. siliconcompiler/apps/sc_server.py +39 -0
  12. siliconcompiler/apps/sc_show.py +142 -0
  13. siliconcompiler/apps/smake.py +232 -0
  14. siliconcompiler/checklists/__init__.py +0 -0
  15. siliconcompiler/checklists/oh_tapeout.py +41 -0
  16. siliconcompiler/core.py +3221 -0
  17. siliconcompiler/data/RobotoMono/LICENSE.txt +202 -0
  18. siliconcompiler/data/RobotoMono/RobotoMono-Regular.ttf +0 -0
  19. siliconcompiler/data/heartbeat.v +18 -0
  20. siliconcompiler/data/logo.png +0 -0
  21. siliconcompiler/flowgraph.py +570 -0
  22. siliconcompiler/flows/__init__.py +0 -0
  23. siliconcompiler/flows/_common.py +67 -0
  24. siliconcompiler/flows/asicflow.py +180 -0
  25. siliconcompiler/flows/asictopflow.py +38 -0
  26. siliconcompiler/flows/dvflow.py +86 -0
  27. siliconcompiler/flows/fpgaflow.py +202 -0
  28. siliconcompiler/flows/generate_openroad_rcx.py +66 -0
  29. siliconcompiler/flows/lintflow.py +35 -0
  30. siliconcompiler/flows/screenshotflow.py +51 -0
  31. siliconcompiler/flows/showflow.py +59 -0
  32. siliconcompiler/flows/signoffflow.py +53 -0
  33. siliconcompiler/flows/synflow.py +128 -0
  34. siliconcompiler/fpgas/__init__.py +0 -0
  35. siliconcompiler/fpgas/lattice_ice40.py +42 -0
  36. siliconcompiler/fpgas/vpr_example.py +109 -0
  37. siliconcompiler/issue.py +300 -0
  38. siliconcompiler/libs/__init__.py +0 -0
  39. siliconcompiler/libs/asap7sc7p5t.py +8 -0
  40. siliconcompiler/libs/gf180mcu.py +8 -0
  41. siliconcompiler/libs/nangate45.py +8 -0
  42. siliconcompiler/libs/sky130hd.py +8 -0
  43. siliconcompiler/libs/sky130io.py +8 -0
  44. siliconcompiler/package.py +412 -0
  45. siliconcompiler/pdks/__init__.py +0 -0
  46. siliconcompiler/pdks/asap7.py +8 -0
  47. siliconcompiler/pdks/freepdk45.py +8 -0
  48. siliconcompiler/pdks/gf180.py +8 -0
  49. siliconcompiler/pdks/skywater130.py +8 -0
  50. siliconcompiler/remote/__init__.py +36 -0
  51. siliconcompiler/remote/client.py +891 -0
  52. siliconcompiler/remote/schema.py +106 -0
  53. siliconcompiler/remote/server.py +507 -0
  54. siliconcompiler/remote/server_schema/requests/cancel_job.json +51 -0
  55. siliconcompiler/remote/server_schema/requests/check_progress.json +61 -0
  56. siliconcompiler/remote/server_schema/requests/check_server.json +38 -0
  57. siliconcompiler/remote/server_schema/requests/delete_job.json +51 -0
  58. siliconcompiler/remote/server_schema/requests/get_results.json +48 -0
  59. siliconcompiler/remote/server_schema/requests/remote_run.json +40 -0
  60. siliconcompiler/remote/server_schema/responses/cancel_job.json +18 -0
  61. siliconcompiler/remote/server_schema/responses/check_progress.json +30 -0
  62. siliconcompiler/remote/server_schema/responses/check_server.json +32 -0
  63. siliconcompiler/remote/server_schema/responses/delete_job.json +18 -0
  64. siliconcompiler/remote/server_schema/responses/get_results.json +21 -0
  65. siliconcompiler/remote/server_schema/responses/remote_run.json +25 -0
  66. siliconcompiler/report/__init__.py +13 -0
  67. siliconcompiler/report/html_report.py +74 -0
  68. siliconcompiler/report/report.py +355 -0
  69. siliconcompiler/report/streamlit_report.py +137 -0
  70. siliconcompiler/report/streamlit_viewer.py +944 -0
  71. siliconcompiler/report/summary_image.py +117 -0
  72. siliconcompiler/report/summary_table.py +105 -0
  73. siliconcompiler/report/utils.py +163 -0
  74. siliconcompiler/scheduler/__init__.py +2092 -0
  75. siliconcompiler/scheduler/docker_runner.py +253 -0
  76. siliconcompiler/scheduler/run_node.py +138 -0
  77. siliconcompiler/scheduler/send_messages.py +178 -0
  78. siliconcompiler/scheduler/slurm.py +208 -0
  79. siliconcompiler/scheduler/validation/email_credentials.json +54 -0
  80. siliconcompiler/schema/__init__.py +7 -0
  81. siliconcompiler/schema/schema_cfg.py +4014 -0
  82. siliconcompiler/schema/schema_obj.py +1841 -0
  83. siliconcompiler/schema/utils.py +93 -0
  84. siliconcompiler/sphinx_ext/__init__.py +0 -0
  85. siliconcompiler/sphinx_ext/dynamicgen.py +1006 -0
  86. siliconcompiler/sphinx_ext/schemagen.py +221 -0
  87. siliconcompiler/sphinx_ext/utils.py +166 -0
  88. siliconcompiler/targets/__init__.py +0 -0
  89. siliconcompiler/targets/asap7_demo.py +68 -0
  90. siliconcompiler/targets/asic_demo.py +38 -0
  91. siliconcompiler/targets/fpgaflow_demo.py +47 -0
  92. siliconcompiler/targets/freepdk45_demo.py +59 -0
  93. siliconcompiler/targets/gf180_demo.py +77 -0
  94. siliconcompiler/targets/skywater130_demo.py +70 -0
  95. siliconcompiler/templates/email/general.j2 +66 -0
  96. siliconcompiler/templates/email/summary.j2 +43 -0
  97. siliconcompiler/templates/issue/README.txt +26 -0
  98. siliconcompiler/templates/issue/run.sh +6 -0
  99. siliconcompiler/templates/report/bootstrap.min.css +7 -0
  100. siliconcompiler/templates/report/bootstrap.min.js +7 -0
  101. siliconcompiler/templates/report/bootstrap_LICENSE.md +24 -0
  102. siliconcompiler/templates/report/sc_report.j2 +427 -0
  103. siliconcompiler/templates/slurm/run.sh +9 -0
  104. siliconcompiler/templates/tcl/manifest.tcl.j2 +137 -0
  105. siliconcompiler/tools/__init__.py +0 -0
  106. siliconcompiler/tools/_common/__init__.py +432 -0
  107. siliconcompiler/tools/_common/asic.py +115 -0
  108. siliconcompiler/tools/_common/sdc/sc_constraints.sdc +76 -0
  109. siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +63 -0
  110. siliconcompiler/tools/bambu/bambu.py +32 -0
  111. siliconcompiler/tools/bambu/convert.py +77 -0
  112. siliconcompiler/tools/bluespec/bluespec.py +40 -0
  113. siliconcompiler/tools/bluespec/convert.py +103 -0
  114. siliconcompiler/tools/builtin/_common.py +155 -0
  115. siliconcompiler/tools/builtin/builtin.py +26 -0
  116. siliconcompiler/tools/builtin/concatenate.py +85 -0
  117. siliconcompiler/tools/builtin/join.py +27 -0
  118. siliconcompiler/tools/builtin/maximum.py +46 -0
  119. siliconcompiler/tools/builtin/minimum.py +57 -0
  120. siliconcompiler/tools/builtin/mux.py +70 -0
  121. siliconcompiler/tools/builtin/nop.py +38 -0
  122. siliconcompiler/tools/builtin/verify.py +83 -0
  123. siliconcompiler/tools/chisel/SCDriver.scala +10 -0
  124. siliconcompiler/tools/chisel/build.sbt +27 -0
  125. siliconcompiler/tools/chisel/chisel.py +37 -0
  126. siliconcompiler/tools/chisel/convert.py +140 -0
  127. siliconcompiler/tools/execute/exec_input.py +41 -0
  128. siliconcompiler/tools/execute/execute.py +17 -0
  129. siliconcompiler/tools/genfasm/bitstream.py +61 -0
  130. siliconcompiler/tools/genfasm/genfasm.py +40 -0
  131. siliconcompiler/tools/ghdl/convert.py +87 -0
  132. siliconcompiler/tools/ghdl/ghdl.py +41 -0
  133. siliconcompiler/tools/icarus/compile.py +87 -0
  134. siliconcompiler/tools/icarus/icarus.py +36 -0
  135. siliconcompiler/tools/icepack/bitstream.py +20 -0
  136. siliconcompiler/tools/icepack/icepack.py +43 -0
  137. siliconcompiler/tools/klayout/export.py +117 -0
  138. siliconcompiler/tools/klayout/klayout.py +119 -0
  139. siliconcompiler/tools/klayout/klayout_export.py +205 -0
  140. siliconcompiler/tools/klayout/klayout_operations.py +363 -0
  141. siliconcompiler/tools/klayout/klayout_show.py +242 -0
  142. siliconcompiler/tools/klayout/klayout_utils.py +176 -0
  143. siliconcompiler/tools/klayout/operations.py +194 -0
  144. siliconcompiler/tools/klayout/screenshot.py +98 -0
  145. siliconcompiler/tools/klayout/show.py +101 -0
  146. siliconcompiler/tools/magic/drc.py +49 -0
  147. siliconcompiler/tools/magic/extspice.py +19 -0
  148. siliconcompiler/tools/magic/magic.py +85 -0
  149. siliconcompiler/tools/magic/sc_drc.tcl +96 -0
  150. siliconcompiler/tools/magic/sc_extspice.tcl +54 -0
  151. siliconcompiler/tools/magic/sc_magic.tcl +47 -0
  152. siliconcompiler/tools/montage/montage.py +30 -0
  153. siliconcompiler/tools/montage/tile.py +66 -0
  154. siliconcompiler/tools/netgen/count_lvs.py +132 -0
  155. siliconcompiler/tools/netgen/lvs.py +90 -0
  156. siliconcompiler/tools/netgen/netgen.py +36 -0
  157. siliconcompiler/tools/netgen/sc_lvs.tcl +46 -0
  158. siliconcompiler/tools/nextpnr/apr.py +24 -0
  159. siliconcompiler/tools/nextpnr/nextpnr.py +59 -0
  160. siliconcompiler/tools/openfpgaloader/openfpgaloader.py +39 -0
  161. siliconcompiler/tools/openroad/__init__.py +0 -0
  162. siliconcompiler/tools/openroad/cts.py +45 -0
  163. siliconcompiler/tools/openroad/dfm.py +66 -0
  164. siliconcompiler/tools/openroad/export.py +131 -0
  165. siliconcompiler/tools/openroad/floorplan.py +70 -0
  166. siliconcompiler/tools/openroad/openroad.py +977 -0
  167. siliconcompiler/tools/openroad/physyn.py +27 -0
  168. siliconcompiler/tools/openroad/place.py +41 -0
  169. siliconcompiler/tools/openroad/rcx_bench.py +95 -0
  170. siliconcompiler/tools/openroad/rcx_extract.py +34 -0
  171. siliconcompiler/tools/openroad/route.py +45 -0
  172. siliconcompiler/tools/openroad/screenshot.py +60 -0
  173. siliconcompiler/tools/openroad/scripts/sc_apr.tcl +499 -0
  174. siliconcompiler/tools/openroad/scripts/sc_cts.tcl +64 -0
  175. siliconcompiler/tools/openroad/scripts/sc_dfm.tcl +20 -0
  176. siliconcompiler/tools/openroad/scripts/sc_export.tcl +98 -0
  177. siliconcompiler/tools/openroad/scripts/sc_floorplan.tcl +413 -0
  178. siliconcompiler/tools/openroad/scripts/sc_metrics.tcl +158 -0
  179. siliconcompiler/tools/openroad/scripts/sc_physyn.tcl +7 -0
  180. siliconcompiler/tools/openroad/scripts/sc_place.tcl +84 -0
  181. siliconcompiler/tools/openroad/scripts/sc_procs.tcl +423 -0
  182. siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +63 -0
  183. siliconcompiler/tools/openroad/scripts/sc_rcx_bench.tcl +20 -0
  184. siliconcompiler/tools/openroad/scripts/sc_rcx_extract.tcl +12 -0
  185. siliconcompiler/tools/openroad/scripts/sc_route.tcl +133 -0
  186. siliconcompiler/tools/openroad/scripts/sc_screenshot.tcl +21 -0
  187. siliconcompiler/tools/openroad/scripts/sc_write.tcl +5 -0
  188. siliconcompiler/tools/openroad/scripts/sc_write_images.tcl +361 -0
  189. siliconcompiler/tools/openroad/show.py +94 -0
  190. siliconcompiler/tools/openroad/templates/pex.tcl +8 -0
  191. siliconcompiler/tools/opensta/__init__.py +101 -0
  192. siliconcompiler/tools/opensta/report_libraries.py +28 -0
  193. siliconcompiler/tools/opensta/scripts/sc_procs.tcl +47 -0
  194. siliconcompiler/tools/opensta/scripts/sc_report_libraries.tcl +74 -0
  195. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +268 -0
  196. siliconcompiler/tools/opensta/timing.py +214 -0
  197. siliconcompiler/tools/slang/__init__.py +49 -0
  198. siliconcompiler/tools/slang/lint.py +101 -0
  199. siliconcompiler/tools/surelog/__init__.py +123 -0
  200. siliconcompiler/tools/surelog/parse.py +183 -0
  201. siliconcompiler/tools/surelog/templates/output.v +7 -0
  202. siliconcompiler/tools/sv2v/convert.py +46 -0
  203. siliconcompiler/tools/sv2v/sv2v.py +37 -0
  204. siliconcompiler/tools/template/template.py +125 -0
  205. siliconcompiler/tools/verilator/compile.py +139 -0
  206. siliconcompiler/tools/verilator/lint.py +19 -0
  207. siliconcompiler/tools/verilator/parse.py +27 -0
  208. siliconcompiler/tools/verilator/verilator.py +172 -0
  209. siliconcompiler/tools/vivado/__init__.py +7 -0
  210. siliconcompiler/tools/vivado/bitstream.py +21 -0
  211. siliconcompiler/tools/vivado/place.py +21 -0
  212. siliconcompiler/tools/vivado/route.py +21 -0
  213. siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +6 -0
  214. siliconcompiler/tools/vivado/scripts/sc_place.tcl +2 -0
  215. siliconcompiler/tools/vivado/scripts/sc_route.tcl +4 -0
  216. siliconcompiler/tools/vivado/scripts/sc_run.tcl +45 -0
  217. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +25 -0
  218. siliconcompiler/tools/vivado/syn_fpga.py +20 -0
  219. siliconcompiler/tools/vivado/vivado.py +147 -0
  220. siliconcompiler/tools/vpr/_json_constraint.py +63 -0
  221. siliconcompiler/tools/vpr/_xml_constraint.py +109 -0
  222. siliconcompiler/tools/vpr/place.py +137 -0
  223. siliconcompiler/tools/vpr/route.py +124 -0
  224. siliconcompiler/tools/vpr/screenshot.py +54 -0
  225. siliconcompiler/tools/vpr/show.py +88 -0
  226. siliconcompiler/tools/vpr/vpr.py +357 -0
  227. siliconcompiler/tools/xyce/xyce.py +36 -0
  228. siliconcompiler/tools/yosys/lec.py +56 -0
  229. siliconcompiler/tools/yosys/prepareLib.py +59 -0
  230. siliconcompiler/tools/yosys/sc_lec.tcl +84 -0
  231. siliconcompiler/tools/yosys/sc_syn.tcl +79 -0
  232. siliconcompiler/tools/yosys/syn_asic.py +565 -0
  233. siliconcompiler/tools/yosys/syn_asic.tcl +377 -0
  234. siliconcompiler/tools/yosys/syn_asic_fpga_shared.tcl +31 -0
  235. siliconcompiler/tools/yosys/syn_fpga.py +146 -0
  236. siliconcompiler/tools/yosys/syn_fpga.tcl +233 -0
  237. siliconcompiler/tools/yosys/syn_strategies.tcl +81 -0
  238. siliconcompiler/tools/yosys/techmaps/lcu_kogge_stone.v +39 -0
  239. siliconcompiler/tools/yosys/templates/abc.const +2 -0
  240. siliconcompiler/tools/yosys/yosys.py +147 -0
  241. siliconcompiler/units.py +259 -0
  242. siliconcompiler/use.py +177 -0
  243. siliconcompiler/utils/__init__.py +423 -0
  244. siliconcompiler/utils/asic.py +158 -0
  245. siliconcompiler/utils/showtools.py +25 -0
  246. siliconcompiler-0.26.5.dist-info/LICENSE +190 -0
  247. siliconcompiler-0.26.5.dist-info/METADATA +195 -0
  248. siliconcompiler-0.26.5.dist-info/RECORD +251 -0
  249. siliconcompiler-0.26.5.dist-info/WHEEL +5 -0
  250. siliconcompiler-0.26.5.dist-info/entry_points.txt +12 -0
  251. 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")