siliconcompiler 0.32.3__py3-none-any.whl → 0.33.1__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 (280) hide show
  1. siliconcompiler/__init__.py +19 -2
  2. siliconcompiler/_common.py +5 -0
  3. siliconcompiler/_metadata.py +1 -1
  4. siliconcompiler/apps/sc.py +2 -2
  5. siliconcompiler/apps/sc_install.py +10 -3
  6. siliconcompiler/apps/sc_issue.py +1 -1
  7. siliconcompiler/apps/sc_remote.py +10 -5
  8. siliconcompiler/apps/sc_show.py +2 -2
  9. siliconcompiler/apps/utils/replay.py +5 -3
  10. siliconcompiler/asic.py +120 -0
  11. siliconcompiler/checklist.py +150 -0
  12. siliconcompiler/core.py +299 -299
  13. siliconcompiler/flowgraph.py +803 -515
  14. siliconcompiler/fpga.py +84 -0
  15. siliconcompiler/metric.py +479 -0
  16. siliconcompiler/optimizer/vizier.py +2 -3
  17. siliconcompiler/package/__init__.py +29 -6
  18. siliconcompiler/pdk.py +415 -0
  19. siliconcompiler/record.py +453 -0
  20. siliconcompiler/remote/client.py +15 -5
  21. siliconcompiler/remote/schema.py +116 -112
  22. siliconcompiler/remote/server.py +9 -6
  23. siliconcompiler/report/dashboard/cli/__init__.py +14 -721
  24. siliconcompiler/report/dashboard/cli/board.py +899 -0
  25. siliconcompiler/report/dashboard/web/__init__.py +10 -10
  26. siliconcompiler/report/dashboard/web/components/__init__.py +5 -4
  27. siliconcompiler/report/dashboard/web/components/flowgraph.py +3 -3
  28. siliconcompiler/report/dashboard/web/components/graph.py +6 -3
  29. siliconcompiler/report/dashboard/web/state.py +1 -1
  30. siliconcompiler/report/dashboard/web/utils/__init__.py +4 -3
  31. siliconcompiler/report/html_report.py +2 -3
  32. siliconcompiler/report/report.py +22 -11
  33. siliconcompiler/report/summary_image.py +1 -1
  34. siliconcompiler/report/summary_table.py +3 -3
  35. siliconcompiler/report/utils.py +21 -14
  36. siliconcompiler/scheduler/__init__.py +234 -1206
  37. siliconcompiler/scheduler/run_node.py +2 -1
  38. siliconcompiler/scheduler/send_messages.py +11 -5
  39. siliconcompiler/scheduler/slurm.py +11 -44
  40. siliconcompiler/scheduler/taskscheduler.py +320 -0
  41. siliconcompiler/schema/__init__.py +19 -2
  42. siliconcompiler/schema/baseschema.py +493 -0
  43. siliconcompiler/schema/cmdlineschema.py +250 -0
  44. siliconcompiler/{sphinx_ext → schema/docs}/__init__.py +3 -1
  45. siliconcompiler/{sphinx_ext → schema/docs}/dynamicgen.py +63 -81
  46. siliconcompiler/{sphinx_ext → schema/docs}/schemagen.py +73 -85
  47. siliconcompiler/{sphinx_ext → schema/docs}/utils.py +12 -13
  48. siliconcompiler/schema/editableschema.py +136 -0
  49. siliconcompiler/schema/journalingschema.py +238 -0
  50. siliconcompiler/schema/namedschema.py +41 -0
  51. siliconcompiler/schema/packageschema.py +101 -0
  52. siliconcompiler/schema/parameter.py +791 -0
  53. siliconcompiler/schema/parametertype.py +323 -0
  54. siliconcompiler/schema/parametervalue.py +736 -0
  55. siliconcompiler/schema/safeschema.py +37 -0
  56. siliconcompiler/schema/schema_cfg.py +109 -1789
  57. siliconcompiler/schema/utils.py +5 -68
  58. siliconcompiler/schema_obj.py +119 -0
  59. siliconcompiler/tool.py +1416 -0
  60. siliconcompiler/tools/_common/__init__.py +6 -10
  61. siliconcompiler/tools/_common/asic.py +5 -5
  62. siliconcompiler/tools/_common/sdc/sc_constraints.sdc +1 -1
  63. siliconcompiler/tools/bluespec/convert.py +9 -8
  64. siliconcompiler/tools/builtin/_common.py +9 -2
  65. siliconcompiler/tools/builtin/concatenate.py +7 -3
  66. siliconcompiler/tools/builtin/minimum.py +7 -2
  67. siliconcompiler/tools/builtin/mux.py +8 -2
  68. siliconcompiler/tools/builtin/nop.py +7 -2
  69. siliconcompiler/tools/builtin/verify.py +11 -5
  70. siliconcompiler/tools/chisel/convert.py +10 -10
  71. siliconcompiler/tools/genfasm/bitstream.py +3 -3
  72. siliconcompiler/tools/ghdl/convert.py +1 -1
  73. siliconcompiler/tools/icarus/compile.py +4 -4
  74. siliconcompiler/tools/icepack/bitstream.py +6 -1
  75. siliconcompiler/tools/klayout/convert_drc_db.py +5 -0
  76. siliconcompiler/tools/klayout/drc.py +2 -2
  77. siliconcompiler/tools/klayout/klayout_export.py +0 -1
  78. siliconcompiler/tools/klayout/klayout_show.py +6 -6
  79. siliconcompiler/tools/klayout/klayout_utils.py +15 -22
  80. siliconcompiler/tools/netgen/count_lvs.py +2 -2
  81. siliconcompiler/tools/netgen/lvs.py +1 -1
  82. siliconcompiler/tools/nextpnr/apr.py +6 -1
  83. siliconcompiler/tools/nextpnr/nextpnr.py +4 -4
  84. siliconcompiler/tools/openroad/_apr.py +15 -2
  85. siliconcompiler/tools/openroad/rdlroute.py +3 -3
  86. siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +1 -1
  87. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +5 -5
  88. siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +2 -2
  89. siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +2 -2
  90. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +2 -2
  91. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +2 -2
  92. siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +2 -2
  93. siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +2 -2
  94. siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +2 -2
  95. siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +2 -2
  96. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -2
  97. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -9
  98. siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +3 -3
  99. siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +2 -2
  100. siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +2 -2
  101. siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +2 -2
  102. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +2 -2
  103. siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +2 -2
  104. siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +2 -2
  105. siliconcompiler/tools/openroad/scripts/common/procs.tcl +75 -1
  106. siliconcompiler/tools/openroad/scripts/common/read_input_files.tcl +1 -7
  107. siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +2 -2
  108. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -3
  109. siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +1 -1
  110. siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -3
  111. siliconcompiler/tools/openroad/scripts/sc_show.tcl +6 -6
  112. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +10 -0
  113. siliconcompiler/tools/opensta/timing.py +11 -0
  114. siliconcompiler/tools/slang/__init__.py +13 -13
  115. siliconcompiler/tools/slang/elaborate.py +6 -6
  116. siliconcompiler/tools/slang/lint.py +1 -3
  117. siliconcompiler/tools/surelog/parse.py +4 -4
  118. siliconcompiler/tools/sv2v/convert.py +20 -3
  119. siliconcompiler/tools/verilator/compile.py +2 -2
  120. siliconcompiler/tools/verilator/verilator.py +3 -3
  121. siliconcompiler/tools/vpr/_xml_constraint.py +8 -8
  122. siliconcompiler/tools/vpr/place.py +1 -1
  123. siliconcompiler/tools/vpr/route.py +4 -4
  124. siliconcompiler/tools/vpr/screenshot.py +1 -1
  125. siliconcompiler/tools/vpr/show.py +5 -5
  126. siliconcompiler/tools/vpr/vpr.py +24 -24
  127. siliconcompiler/tools/xdm/convert.py +2 -2
  128. siliconcompiler/tools/xyce/simulate.py +1 -1
  129. siliconcompiler/tools/yosys/prepareLib.py +2 -2
  130. siliconcompiler/tools/yosys/sc_synth_asic.tcl +111 -63
  131. siliconcompiler/tools/yosys/screenshot.py +1 -1
  132. siliconcompiler/tools/yosys/syn_asic.py +7 -7
  133. siliconcompiler/toolscripts/_tools.json +12 -10
  134. siliconcompiler/toolscripts/rhel8/install-chisel.sh +9 -2
  135. siliconcompiler/toolscripts/rhel8/install-icarus.sh +10 -3
  136. siliconcompiler/toolscripts/rhel8/install-klayout.sh +8 -1
  137. siliconcompiler/toolscripts/rhel8/install-magic.sh +9 -2
  138. siliconcompiler/toolscripts/rhel8/install-montage.sh +1 -1
  139. siliconcompiler/toolscripts/rhel8/install-netgen.sh +9 -2
  140. siliconcompiler/toolscripts/rhel8/install-slang.sh +11 -4
  141. siliconcompiler/toolscripts/rhel8/install-surelog.sh +9 -2
  142. siliconcompiler/toolscripts/rhel8/install-sv2v.sh +11 -4
  143. siliconcompiler/toolscripts/rhel8/install-verible.sh +11 -3
  144. siliconcompiler/toolscripts/rhel8/install-verilator.sh +10 -3
  145. siliconcompiler/toolscripts/rhel8/install-xyce.sh +15 -10
  146. siliconcompiler/toolscripts/rhel9/install-chisel.sh +9 -2
  147. siliconcompiler/toolscripts/rhel9/install-ghdl.sh +9 -2
  148. siliconcompiler/toolscripts/rhel9/install-gtkwave.sh +10 -3
  149. siliconcompiler/toolscripts/rhel9/install-icarus.sh +10 -3
  150. siliconcompiler/toolscripts/rhel9/install-klayout.sh +8 -1
  151. siliconcompiler/toolscripts/rhel9/install-magic.sh +9 -2
  152. siliconcompiler/toolscripts/rhel9/install-montage.sh +1 -1
  153. siliconcompiler/toolscripts/rhel9/install-netgen.sh +9 -2
  154. siliconcompiler/toolscripts/rhel9/install-openroad.sh +16 -3
  155. siliconcompiler/toolscripts/rhel9/install-opensta.sh +17 -5
  156. siliconcompiler/toolscripts/rhel9/install-slang.sh +11 -4
  157. siliconcompiler/toolscripts/rhel9/install-surelog.sh +9 -2
  158. siliconcompiler/toolscripts/rhel9/install-sv2v.sh +11 -4
  159. siliconcompiler/toolscripts/rhel9/install-verible.sh +11 -3
  160. siliconcompiler/toolscripts/rhel9/install-verilator.sh +10 -3
  161. siliconcompiler/toolscripts/rhel9/install-vpr.sh +9 -2
  162. siliconcompiler/toolscripts/rhel9/install-xdm.sh +10 -2
  163. siliconcompiler/toolscripts/rhel9/install-xyce.sh +15 -10
  164. siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +9 -2
  165. siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +10 -3
  166. siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +10 -2
  167. siliconcompiler/toolscripts/rhel9/install-yosys.sh +9 -2
  168. siliconcompiler/toolscripts/ubuntu20/install-bambu.sh +10 -2
  169. siliconcompiler/toolscripts/ubuntu20/install-bluespec.sh +10 -3
  170. siliconcompiler/toolscripts/ubuntu20/install-chisel.sh +9 -2
  171. siliconcompiler/toolscripts/ubuntu20/install-ghdl.sh +9 -2
  172. siliconcompiler/toolscripts/ubuntu20/install-gtkwave.sh +9 -2
  173. siliconcompiler/toolscripts/ubuntu20/install-icarus.sh +9 -2
  174. siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +9 -2
  175. siliconcompiler/toolscripts/ubuntu20/install-klayout.sh +8 -1
  176. siliconcompiler/toolscripts/ubuntu20/install-magic.sh +9 -2
  177. siliconcompiler/toolscripts/ubuntu20/install-montage.sh +1 -1
  178. siliconcompiler/toolscripts/ubuntu20/install-netgen.sh +9 -2
  179. siliconcompiler/toolscripts/ubuntu20/install-nextpnr.sh +9 -2
  180. siliconcompiler/toolscripts/ubuntu20/install-openroad.sh +16 -3
  181. siliconcompiler/toolscripts/ubuntu20/install-opensta.sh +16 -5
  182. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +11 -4
  183. siliconcompiler/toolscripts/ubuntu20/install-slurm.sh +9 -2
  184. siliconcompiler/toolscripts/ubuntu20/install-surelog.sh +10 -2
  185. siliconcompiler/toolscripts/ubuntu20/install-sv2v.sh +11 -4
  186. siliconcompiler/toolscripts/ubuntu20/install-verible.sh +11 -3
  187. siliconcompiler/toolscripts/ubuntu20/install-verilator.sh +9 -2
  188. siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +10 -2
  189. siliconcompiler/toolscripts/ubuntu20/install-xyce.sh +13 -8
  190. siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +9 -2
  191. siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +9 -2
  192. siliconcompiler/toolscripts/ubuntu22/install-bambu.sh +10 -2
  193. siliconcompiler/toolscripts/ubuntu22/install-bluespec.sh +10 -3
  194. siliconcompiler/toolscripts/ubuntu22/install-chisel.sh +9 -2
  195. siliconcompiler/toolscripts/ubuntu22/install-ghdl.sh +9 -2
  196. siliconcompiler/toolscripts/ubuntu22/install-gtkwave.sh +9 -2
  197. siliconcompiler/toolscripts/ubuntu22/install-icarus.sh +9 -2
  198. siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +9 -2
  199. siliconcompiler/toolscripts/ubuntu22/install-klayout.sh +8 -1
  200. siliconcompiler/toolscripts/ubuntu22/install-magic.sh +9 -2
  201. siliconcompiler/toolscripts/ubuntu22/install-montage.sh +1 -1
  202. siliconcompiler/toolscripts/ubuntu22/install-netgen.sh +9 -2
  203. siliconcompiler/toolscripts/ubuntu22/install-nextpnr.sh +9 -2
  204. siliconcompiler/toolscripts/ubuntu22/install-openroad.sh +16 -3
  205. siliconcompiler/toolscripts/ubuntu22/install-opensta.sh +17 -5
  206. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +11 -4
  207. siliconcompiler/toolscripts/ubuntu22/install-slurm.sh +9 -2
  208. siliconcompiler/toolscripts/ubuntu22/install-surelog.sh +10 -2
  209. siliconcompiler/toolscripts/ubuntu22/install-sv2v.sh +11 -4
  210. siliconcompiler/toolscripts/ubuntu22/install-verible.sh +11 -3
  211. siliconcompiler/toolscripts/ubuntu22/install-verilator.sh +9 -2
  212. siliconcompiler/toolscripts/ubuntu22/install-vpr.sh +9 -4
  213. siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +10 -2
  214. siliconcompiler/toolscripts/ubuntu22/install-xyce.sh +13 -8
  215. siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +9 -2
  216. siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +10 -3
  217. siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +10 -2
  218. siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +9 -2
  219. siliconcompiler/toolscripts/ubuntu24/install-bambu.sh +12 -4
  220. siliconcompiler/toolscripts/ubuntu24/install-bluespec.sh +10 -3
  221. siliconcompiler/toolscripts/ubuntu24/install-chisel.sh +9 -2
  222. siliconcompiler/toolscripts/ubuntu24/install-ghdl.sh +9 -2
  223. siliconcompiler/toolscripts/ubuntu24/install-gtkwave.sh +9 -2
  224. siliconcompiler/toolscripts/ubuntu24/install-icarus.sh +9 -2
  225. siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +9 -2
  226. siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +8 -1
  227. siliconcompiler/toolscripts/ubuntu24/install-magic.sh +9 -2
  228. siliconcompiler/toolscripts/ubuntu24/install-montage.sh +1 -1
  229. siliconcompiler/toolscripts/ubuntu24/install-netgen.sh +9 -2
  230. siliconcompiler/toolscripts/ubuntu24/install-nextpnr.sh +9 -2
  231. siliconcompiler/toolscripts/ubuntu24/install-openroad.sh +16 -3
  232. siliconcompiler/toolscripts/ubuntu24/install-opensta.sh +17 -5
  233. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +11 -4
  234. siliconcompiler/toolscripts/ubuntu24/install-slurm.sh +9 -2
  235. siliconcompiler/toolscripts/ubuntu24/install-surelog.sh +10 -2
  236. siliconcompiler/toolscripts/ubuntu24/install-sv2v.sh +11 -4
  237. siliconcompiler/toolscripts/ubuntu24/install-verible.sh +11 -3
  238. siliconcompiler/toolscripts/ubuntu24/install-verilator.sh +9 -2
  239. siliconcompiler/toolscripts/ubuntu24/install-vpr.sh +9 -4
  240. siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +10 -2
  241. siliconcompiler/toolscripts/ubuntu24/install-xyce.sh +13 -8
  242. siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +9 -2
  243. siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +10 -3
  244. siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +10 -2
  245. siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +9 -2
  246. siliconcompiler/utils/__init__.py +19 -112
  247. siliconcompiler/utils/flowgraph.py +244 -0
  248. siliconcompiler/{issue.py → utils/issue.py} +18 -25
  249. siliconcompiler/utils/logging.py +3 -4
  250. {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/METADATA +9 -8
  251. siliconcompiler-0.33.1.dist-info/RECORD +488 -0
  252. {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/WHEEL +1 -1
  253. {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/entry_points.txt +8 -8
  254. siliconcompiler/schema/schema_obj.py +0 -1936
  255. siliconcompiler/toolscripts/ubuntu20/install-vpr.sh +0 -29
  256. siliconcompiler/toolscripts/ubuntu20/install-yosys-parmys.sh +0 -61
  257. siliconcompiler-0.32.3.dist-info/RECORD +0 -470
  258. /siliconcompiler/{templates → data/templates}/__init__.py +0 -0
  259. /siliconcompiler/{templates → data/templates}/email/__init__.py +0 -0
  260. /siliconcompiler/{templates → data/templates}/email/general.j2 +0 -0
  261. /siliconcompiler/{templates → data/templates}/email/summary.j2 +0 -0
  262. /siliconcompiler/{templates → data/templates}/issue/README.txt +0 -0
  263. /siliconcompiler/{templates → data/templates}/issue/__init__.py +0 -0
  264. /siliconcompiler/{templates → data/templates}/issue/run.sh +0 -0
  265. /siliconcompiler/{templates → data/templates}/replay/replay.py.j2 +0 -0
  266. /siliconcompiler/{templates → data/templates}/replay/replay.sh.j2 +0 -0
  267. /siliconcompiler/{templates → data/templates}/replay/requirements.txt +0 -0
  268. /siliconcompiler/{templates → data/templates}/replay/setup.sh +0 -0
  269. /siliconcompiler/{templates → data/templates}/report/__init__.py +0 -0
  270. /siliconcompiler/{templates → data/templates}/report/bootstrap.min.css +0 -0
  271. /siliconcompiler/{templates → data/templates}/report/bootstrap.min.js +0 -0
  272. /siliconcompiler/{templates → data/templates}/report/bootstrap_LICENSE.md +0 -0
  273. /siliconcompiler/{templates → data/templates}/report/sc_report.j2 +0 -0
  274. /siliconcompiler/{templates → data/templates}/slurm/__init__.py +0 -0
  275. /siliconcompiler/{templates → data/templates}/slurm/run.sh +0 -0
  276. /siliconcompiler/{templates → data/templates}/tcl/__init__.py +0 -0
  277. /siliconcompiler/{templates → data/templates}/tcl/manifest.tcl.j2 +0 -0
  278. /siliconcompiler/{units.py → utils/units.py} +0 -0
  279. {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/licenses/LICENSE +0 -0
  280. {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1416 @@
1
+ import contextlib
2
+ import logging
3
+ import os
4
+ import psutil
5
+ import re
6
+ import shlex
7
+ import shutil
8
+ import subprocess
9
+ import sys
10
+ import time
11
+
12
+ try:
13
+ import resource
14
+ except ModuleNotFoundError:
15
+ resource = None
16
+
17
+ try:
18
+ # Note: this import throws exception on Windows
19
+ import pty
20
+ except ModuleNotFoundError:
21
+ pty = None
22
+
23
+ import os.path
24
+
25
+ from packaging.version import Version, InvalidVersion
26
+ from packaging.specifiers import SpecifierSet, InvalidSpecifier
27
+
28
+ from siliconcompiler.schema import NamedSchema
29
+ from siliconcompiler.schema import EditableSchema, Parameter, PerNode, Scope
30
+ from siliconcompiler.schema.utils import trim
31
+
32
+ from siliconcompiler import utils
33
+ from siliconcompiler import sc_open
34
+
35
+ from siliconcompiler.record import RecordTool
36
+ from siliconcompiler.flowgraph import RuntimeFlowgraph
37
+
38
+
39
+ class TaskError(Exception):
40
+ '''
41
+ Error indicates execution cannot continue and should be terminated
42
+ '''
43
+
44
+
45
+ class TaskTimeout(TaskError):
46
+ '''
47
+ Error indicates a timeout has occurred
48
+
49
+ Args:
50
+ timeout (float): execution time at timeout
51
+ '''
52
+ def __init__(self, *args, timeout=None, **kwargs):
53
+ super().__init__(*args, **kwargs)
54
+ self.timeout = timeout
55
+
56
+
57
+ class TaskExecutableNotFound(TaskError):
58
+ '''
59
+ Executable not found.
60
+ '''
61
+
62
+
63
+ class TaskSchema(NamedSchema):
64
+ def __init__(self, name=None):
65
+ super().__init__(name=name)
66
+
67
+ schema_task(self)
68
+
69
+
70
+ class ToolSchema(NamedSchema):
71
+ __parse_version_check_str = r"""
72
+ (?P<operator>(==|!=|<=|>=|<|>|~=))
73
+ \s*
74
+ (?P<version>
75
+ [^,;\s)]* # Since this is a "legacy" specifier, and the version
76
+ # string can be just about anything, we match everything
77
+ # except for whitespace, a semi-colon for marker support,
78
+ # a closing paren since versions can be enclosed in
79
+ # them, and a comma since it's a version separator.
80
+ )
81
+ """
82
+ __parse_version_check = re.compile(
83
+ r"^\s*" + __parse_version_check_str + r"\s*$",
84
+ re.VERBOSE | re.IGNORECASE)
85
+
86
+ def __init__(self, name=None):
87
+ super().__init__(name=name)
88
+
89
+ schema_tool(self)
90
+
91
+ schema = EditableSchema(self)
92
+ schema.insert("task", "default", TaskSchema())
93
+
94
+ self.set_runtime(None)
95
+
96
+ def set_runtime(self, chip, step=None, index=None):
97
+ '''
98
+ Sets the runtime information needed to properly execute a task.
99
+ Note: unstable API
100
+
101
+ Args:
102
+ chip (:class:`Chip`): root schema for the runtime information
103
+ '''
104
+ self.__chip = None
105
+ self.__schema_full = None
106
+ self.__logger = None
107
+ if chip:
108
+ self.__chip = chip
109
+ self.__schema_full = chip.schema
110
+ self.__logger = chip.logger
111
+
112
+ self.__step = step
113
+ self.__index = index
114
+ self.__tool = None
115
+ self.__task = None
116
+
117
+ self.__schema_record = None
118
+ self.__schema_metric = None
119
+ self.__schema_flow = None
120
+ if self.__schema_full:
121
+ self.__schema_record = self.__schema_full.get("record", field="schema")
122
+ self.__schema_metric = self.__schema_full.get("metric", field="schema")
123
+
124
+ if not self.__step:
125
+ self.__step = self.__schema_full.get('arg', 'step')
126
+ if not self.__index:
127
+ self.__index = self.__schema_full.get('arg', 'index')
128
+
129
+ if not self.__step or not self.__index:
130
+ raise RuntimeError("step or index not specified")
131
+
132
+ flow = self.__schema_full.get('option', 'flow')
133
+ if not flow:
134
+ raise RuntimeError("flow not specified")
135
+ self.__schema_flow = self.__schema_full.get("flowgraph", flow, field="schema")
136
+ self.__tool = self.__schema_flow.get(self.__step, self.__index, 'tool')
137
+ self.__task = self.__schema_flow.get(self.__step, self.__index, 'task')
138
+
139
+ def node(self):
140
+ '''
141
+ Returns:
142
+ step and index for the current runtime
143
+ '''
144
+
145
+ return self.__step, self.__index
146
+
147
+ def task(self):
148
+ '''
149
+ Returns:
150
+ task name
151
+ '''
152
+
153
+ return self.__task
154
+
155
+ def logger(self):
156
+ '''
157
+ Returns:
158
+ logger
159
+ '''
160
+ return self.__logger
161
+
162
+ def schema(self, type=None):
163
+ '''
164
+ Get useful section of the schema.
165
+
166
+ Args:
167
+ type (str): schema section to find, if None returns the root schema.
168
+
169
+ Returns:
170
+ schema section.
171
+ '''
172
+ if type is None:
173
+ return self.__schema_full
174
+ elif type == "record":
175
+ return self.__schema_record
176
+ elif type == "metric":
177
+ return self.__schema_metric
178
+ elif type == "flow":
179
+ return self.__schema_flow
180
+ else:
181
+ raise ValueError(f"{type} is not a schema section")
182
+
183
+ def get_exe(self):
184
+ '''
185
+ Determines the absolute path for the specified executable.
186
+
187
+ Raises:
188
+ :class:`TaskExecutableNotFound`: if executable not found.
189
+
190
+ Returns:
191
+ path to executable, or None if not specified
192
+ '''
193
+
194
+ exe = self.get('exe')
195
+
196
+ if exe is None:
197
+ return None
198
+
199
+ # Collect path
200
+ env = self.get_runtime_environmental_variables(include_path=True)
201
+
202
+ fullexe = shutil.which(exe, path=env["PATH"])
203
+
204
+ if not fullexe:
205
+ raise TaskExecutableNotFound(f"{exe} could not be found")
206
+
207
+ return fullexe
208
+
209
+ def get_exe_version(self):
210
+ '''
211
+ Gets the version of the specified executable.
212
+
213
+ Raises:
214
+ :class:`TaskExecutableNotFound`: if executable not found.
215
+ :class:`NotImplementedError`: if :meth:`.parse_version` has not be implemented.
216
+
217
+ Returns:
218
+ version determined by :meth:`.parse_version`.
219
+ '''
220
+
221
+ veropt = self.get('vswitch')
222
+ if not veropt:
223
+ return None
224
+
225
+ exe = self.get_exe()
226
+ if not exe:
227
+ return None
228
+
229
+ exe_path, exe_base = os.path.split(exe)
230
+
231
+ cmdlist = [exe]
232
+ cmdlist.extend(veropt)
233
+
234
+ self.__logger.debug(f'Running {self.name()} version check: {" ".join(cmdlist)}')
235
+
236
+ proc = subprocess.run(cmdlist,
237
+ stdin=subprocess.DEVNULL,
238
+ stdout=subprocess.PIPE,
239
+ stderr=subprocess.STDOUT,
240
+ universal_newlines=True)
241
+
242
+ if proc.returncode != 0:
243
+ self.__logger.warning(f"Version check on '{exe_base}' ended with "
244
+ f"code {proc.returncode}")
245
+
246
+ try:
247
+ version = self.parse_version(proc.stdout)
248
+ except NotImplementedError:
249
+ raise NotImplementedError(f'{self.name()} does not implement parse_version()')
250
+ except Exception as e:
251
+ self.__logger.error(f'{self.name()} failed to parse version string: {proc.stdout}')
252
+ raise e from None
253
+
254
+ self.__logger.info(f"Tool '{exe_base}' found with version '{version}' "
255
+ f"in directory '{exe_path}'")
256
+
257
+ return version
258
+
259
+ def check_exe_version(self, reported_version):
260
+ '''
261
+ Check if the reported version matches the versions specified in
262
+ :keypath:`tool,<tool>,version`.
263
+
264
+ Args:
265
+ reported_version (str): version to check
266
+
267
+ Returns:
268
+ True if the version matched, false otherwise
269
+
270
+ '''
271
+
272
+ spec_sets = self.get('version', step=self.__step, index=self.__index)
273
+ if not spec_sets:
274
+ # No requirement so always true
275
+ return True
276
+
277
+ for spec_set in spec_sets:
278
+ split_specs = [s.strip() for s in spec_set.split(",") if s.strip()]
279
+ specs_list = []
280
+ for spec in split_specs:
281
+ match = re.match(ToolSchema.__parse_version_check, spec)
282
+ if match is None:
283
+ self.__logger.warning(f'Invalid version specifier {spec}. '
284
+ f'Defaulting to =={spec}.')
285
+ operator = '=='
286
+ spec_version = spec
287
+ else:
288
+ operator = match.group('operator')
289
+ spec_version = match.group('version')
290
+ specs_list.append((operator, spec_version))
291
+
292
+ try:
293
+ normalized_version = self.normalize_version(reported_version)
294
+ except Exception as e:
295
+ self.__logger.error(f'Unable to normalize version for {self.name()}: '
296
+ f'{reported_version}')
297
+ raise e from None
298
+
299
+ try:
300
+ version = Version(normalized_version)
301
+ except InvalidVersion:
302
+ self.__logger.error(f'Version {normalized_version} reported by {self.name()} does '
303
+ 'not match standard.')
304
+ return False
305
+
306
+ try:
307
+ normalized_spec_list = [
308
+ f'{op}{self.normalize_version(ver)}' for op, ver in specs_list]
309
+ normalized_specs = ','.join(normalized_spec_list)
310
+ except Exception as e:
311
+ self.__logger.error(f'Unable to normalize versions for {self.name()}: '
312
+ f'{",".join([f"{op}{ver}" for op, ver in specs_list])}')
313
+ raise e from None
314
+
315
+ try:
316
+ spec_set = SpecifierSet(normalized_specs)
317
+ except InvalidSpecifier:
318
+ self.__logger.error(f'Version specifier set {normalized_specs} '
319
+ 'does not match standard.')
320
+ return False
321
+
322
+ if version in spec_set:
323
+ return True
324
+
325
+ allowedstr = '; '.join(spec_sets)
326
+ self.__logger.error(f"Version check failed for {self.name()}. Check installation.")
327
+ self.__logger.error(f"Found version {reported_version}, "
328
+ f"did not satisfy any version specifier set {allowedstr}.")
329
+ return False
330
+
331
+ def get_runtime_environmental_variables(self, include_path=True):
332
+ '''
333
+ Determine the environmental variables needed for the task
334
+
335
+ Args:
336
+ include_path (bool): if True, includes PATH variable
337
+
338
+ Returns:
339
+ dict of str: dictionary of environmental variable to value mapping
340
+ '''
341
+
342
+ # Add global environmental vars
343
+ envvars = {}
344
+ for env in self.__schema_full.getkeys('option', 'env'):
345
+ envvars[env] = self.__schema_full.get('option', 'env', env)
346
+
347
+ # Add tool specific vars
348
+ for lic_env in self.getkeys('licenseserver'):
349
+ license_file = self.get('licenseserver', lic_env, step=self.__step, index=self.__index)
350
+ if license_file:
351
+ envvars[lic_env] = ':'.join(license_file)
352
+
353
+ if include_path:
354
+ path_param = self.get('path', field=None, step=self.__step, index=self.__index)
355
+ if path_param.get(field='package'):
356
+ raise NotImplementedError
357
+
358
+ envvars["PATH"] = os.getenv("PATH", os.defpath)
359
+
360
+ path = path_param.get(field=None).resolve_path() # TODO: needs package search
361
+ if path:
362
+ envvars["PATH"] = path + os.pathsep + envvars["PATH"]
363
+
364
+ # Forward additional variables
365
+ for var in ('LD_LIBRARY_PATH',):
366
+ val = os.getenv(var, None)
367
+ if val:
368
+ envvars[var] = val
369
+
370
+ # Add task specific vars
371
+ for env in self.getkeys('task', self.__task, 'env'):
372
+ envvars[env] = self.get('task', self.__task, 'env', env,
373
+ step=self.__step, index=self.__index)
374
+
375
+ return envvars
376
+
377
+ def get_runtime_arguments(self):
378
+ '''
379
+ Constructs the arguments needed to run the task.
380
+
381
+ Returns:
382
+ command (list)
383
+ '''
384
+
385
+ cmdargs = []
386
+ cmdargs.extend(self.get('task', self.__task, 'option',
387
+ step=self.__step, index=self.__index))
388
+
389
+ # Add scripts files / TODO:
390
+ scripts = self.__chip.find_files('tool', self.__tool, 'task', self.__task, 'script',
391
+ step=self.__step, index=self.__index)
392
+
393
+ cmdargs.extend(scripts)
394
+
395
+ try:
396
+ cmdargs.extend(self.runtime_options())
397
+ except Exception as e:
398
+ self.__logger.error(f'Failed to get runtime options for {self.name()}/{self.__task}')
399
+ raise e from None
400
+
401
+ # Cleanup args
402
+ cmdargs = [str(arg).strip() for arg in cmdargs]
403
+
404
+ return cmdargs
405
+
406
+ def generate_replay_script(self, filepath, workdir, include_path=True):
407
+ '''
408
+ Generate a replay script for the task.
409
+
410
+ Args:
411
+ filepath (path): path to the file to write
412
+ workdir (path): path to the run work directory
413
+ include_path (bool): include path information in environmental variables
414
+ '''
415
+ replay_opts = {}
416
+ replay_opts["work_dir"] = workdir
417
+ replay_opts["exports"] = self.get_runtime_environmental_variables(include_path=include_path)
418
+
419
+ replay_opts["executable"] = self.get('exe')
420
+
421
+ vswitch = self.get('vswitch')
422
+ if vswitch:
423
+ replay_opts["version_flag"] = shlex.join(vswitch)
424
+
425
+ # detect arguments
426
+ arg_test = re.compile(r'^[-+]')
427
+
428
+ # detect file paths
429
+ file_test = re.compile(r'^[/\.]')
430
+
431
+ format_cmd = [replay_opts["executable"]]
432
+
433
+ for cmdarg in self.get_runtime_arguments():
434
+ add_new_line = len(format_cmd) == 1
435
+
436
+ if arg_test.match(cmdarg) or file_test.match(cmdarg):
437
+ add_new_line = True
438
+ else:
439
+ if not arg_test.match(format_cmd[-1]):
440
+ add_new_line = True
441
+
442
+ cmdarg = shlex.quote(cmdarg)
443
+ if add_new_line:
444
+ format_cmd.append(cmdarg)
445
+ else:
446
+ format_cmd[-1] += f' {cmdarg}'
447
+
448
+ replay_opts["cmds"] = format_cmd
449
+
450
+ # create replay file
451
+ with open(filepath, 'w') as f:
452
+ f.write(utils.get_file_template("replay/replay.sh.j2").render(replay_opts))
453
+ f.write("\n")
454
+
455
+ os.chmod(filepath, 0o755)
456
+
457
+ def setup_work_directory(self, workdir, remove_exist=True):
458
+ '''
459
+ Create the runtime directories needed to execute a task.
460
+
461
+ Args:
462
+ workdir (path): path to the run work directory
463
+ remove_exist (bool): if True, removes the existing directory
464
+ '''
465
+
466
+ # Delete existing directory
467
+ if os.path.isdir(workdir) and remove_exist:
468
+ shutil.rmtree(workdir)
469
+
470
+ # Create directories
471
+ os.makedirs(workdir, exist_ok=True)
472
+ os.makedirs(os.path.join(workdir, 'inputs'), exist_ok=True)
473
+ os.makedirs(os.path.join(workdir, 'outputs'), exist_ok=True)
474
+ os.makedirs(os.path.join(workdir, 'reports'), exist_ok=True)
475
+
476
+ def write_task_manifest(self, directory, backup=True):
477
+ '''
478
+ Write the manifest needed for the task
479
+
480
+ Args:
481
+ directory (path): directory to write the manifest into.
482
+ backup (bool): if True and an existing manifest is found a backup is kept.
483
+ '''
484
+
485
+ suffix = self.get('format')
486
+ if not suffix:
487
+ return
488
+
489
+ manifest_path = os.path.join(directory, f"sc_manifest.{suffix}")
490
+
491
+ if backup and os.path.exists(manifest_path):
492
+ shutil.copyfile(manifest_path, f'{manifest_path}.bak')
493
+
494
+ # TODO: pull in TCL/yaml here
495
+ self.__chip.write_manifest(manifest_path, abspath=True)
496
+
497
+ def __get_io_file(self, io_type):
498
+ '''
499
+ Get the runtime destination for the io type.
500
+
501
+ Args:
502
+ io_type (str): name of io type
503
+ '''
504
+ suffix = self.get('task', self.__task, io_type, 'suffix',
505
+ step=self.__step, index=self.__index)
506
+ destination = self.get('task', self.__task, io_type, 'destination',
507
+ step=self.__step, index=self.__index)
508
+
509
+ io_file = None
510
+ io_log = False
511
+ if destination == 'log':
512
+ io_file = f"{self.__step}.{suffix}"
513
+ io_log = True
514
+ elif destination == 'output':
515
+ io_file = os.path.join('outputs', f"{self.__chip.top()}.{suffix}")
516
+ elif destination == 'none':
517
+ io_file = os.devnull
518
+
519
+ return io_file, io_log
520
+
521
+ def __terminate_exe(self, proc):
522
+ '''
523
+ Terminates a subprocess
524
+
525
+ Args:
526
+ proc (subprocess.Process): process to terminate
527
+ '''
528
+
529
+ def terminate_process(pid, timeout=3):
530
+ '''Terminates a process and all its (grand+)children.
531
+
532
+ Based on https://psutil.readthedocs.io/en/latest/#psutil.wait_procs and
533
+ https://psutil.readthedocs.io/en/latest/#kill-process-tree.
534
+ '''
535
+ parent = psutil.Process(pid)
536
+ children = parent.children(recursive=True)
537
+ children.append(parent)
538
+ for p in children:
539
+ try:
540
+ p.terminate()
541
+ except psutil.NoSuchProcess:
542
+ # Process may have terminated on its own in the meantime
543
+ pass
544
+
545
+ _, alive = psutil.wait_procs(children, timeout=timeout)
546
+ for p in alive:
547
+ # If processes are still alive after timeout seconds, send more
548
+ # aggressive signal.
549
+ p.kill()
550
+
551
+ TERMINATE_TIMEOUT = 5
552
+
553
+ terminate_process(proc.pid, timeout=TERMINATE_TIMEOUT)
554
+ self.__logger.info(f'Waiting for {self.name()} to exit...')
555
+ try:
556
+ proc.wait(timeout=TERMINATE_TIMEOUT)
557
+ except subprocess.TimeoutExpired:
558
+ if proc.poll() is None:
559
+ self.__logger.warning(f'{self.name()} did not exit within {TERMINATE_TIMEOUT} '
560
+ 'seconds. Terminating...')
561
+ terminate_process(proc.pid, timeout=TERMINATE_TIMEOUT)
562
+
563
+ def run_task(self, workdir, quiet, loglevel, breakpoint, nice, timeout):
564
+ '''
565
+ Run the task.
566
+
567
+ Raises:
568
+ :class:`TaskError`: raised if the task failed to complete and
569
+ should not be considered complete.
570
+ :class:`TaskTimeout`: raised if the task reaches a timeout
571
+
572
+ Args:
573
+ workdir (path): path to the run work directory
574
+ quiet (bool): if True, execution output is suppressed
575
+ loglevel (str): logging level
576
+ breakpoint (bool): if True, will attempt to execute with a breakpoint
577
+ nice (int): POSIX nice level to use in execution
578
+ timeout (int): timeout to use for execution
579
+
580
+ Returns:
581
+ return code from the execution
582
+ '''
583
+
584
+ # TODO: Currently no memory usage tracking in breakpoints, builtins, or unexpected errors.
585
+ max_mem_bytes = 0
586
+ cpu_start = time.time()
587
+
588
+ # Ensure directories are setup
589
+ self.setup_work_directory(workdir, remove_exist=False)
590
+
591
+ # Write task manifest
592
+ self.write_task_manifest(workdir)
593
+
594
+ # Get file IO
595
+ stdout_file, is_stdout_log = self.__get_io_file("stdout")
596
+ stderr_file, is_stderr_log = self.__get_io_file("stderr")
597
+
598
+ stdout_print = self.__logger.info
599
+ stderr_print = self.__logger.error
600
+ if loglevel == "quiet":
601
+ stdout_print = self.__logger.error
602
+ stderr_print = self.__logger.error
603
+
604
+ def read_stdio(stdout_reader, stderr_reader):
605
+ if quiet:
606
+ return
607
+
608
+ if is_stdout_log and stdout_reader:
609
+ for line in stdout_reader.readlines():
610
+ stdout_print(line.rstrip())
611
+ if is_stderr_log and stderr_reader:
612
+ for line in stderr_reader.readlines():
613
+ stderr_print(line.rstrip())
614
+
615
+ exe = self.get_exe()
616
+
617
+ retcode = 0
618
+ if not exe:
619
+ # No executable, so must call run()
620
+ try:
621
+ with open(stdout_file, 'w') as stdout_writer, \
622
+ open(stderr_file, 'w') as stderr_writer:
623
+ if stderr_file == stdout_file:
624
+ stderr_writer.close()
625
+ stderr_writer = sys.stdout
626
+
627
+ with contextlib.redirect_stderr(stderr_writer), \
628
+ contextlib.redirect_stdout(stdout_writer):
629
+ retcode = self.run()
630
+ except Exception as e:
631
+ self.__logger.error(f'Failed in run() for {self.name()}/{self.__task}: {e}')
632
+ utils.print_traceback(self.__logger, e)
633
+ raise e
634
+ finally:
635
+ with sc_open(stdout_file) as stdout_reader, \
636
+ sc_open(stderr_file) as stderr_reader:
637
+ read_stdio(stdout_reader, stderr_reader)
638
+
639
+ if resource:
640
+ try:
641
+ # Since memory collection is not possible, collect the current process
642
+ # peak memory
643
+ max_mem_bytes = max(
644
+ max_mem_bytes,
645
+ 1024 * resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
646
+ except (OSError, ValueError, PermissionError):
647
+ pass
648
+ else:
649
+ cmdlist = self.get_runtime_arguments()
650
+
651
+ # Make record of tool options
652
+ self.schema("record").record_tool(
653
+ self.__step, self.__index,
654
+ cmdlist, RecordTool.ARGS)
655
+
656
+ self.__logger.info(shlex.join([os.path.basename(exe), *cmdlist]))
657
+
658
+ if not pty and breakpoint:
659
+ # pty not available
660
+ breakpoint = False
661
+
662
+ if breakpoint and sys.platform in ('darwin', 'linux'):
663
+ # When we break on a step, the tool often drops into a shell.
664
+ # However, our usual subprocess scheme seems to break terminal
665
+ # echo for some tools. On POSIX-compatible systems, we can use
666
+ # pty to connect the tool to our terminal instead. This code
667
+ # doesn't handle quiet/timeout logic, since we don't want either
668
+ # of these features for an interactive session. Logic for
669
+ # forwarding to file based on
670
+ # https://docs.python.org/3/library/pty.html#example.
671
+ with open(f"{self.__step}.log", 'wb') as log_writer:
672
+ def read(fd):
673
+ data = os.read(fd, 1024)
674
+ log_writer.write(data)
675
+ return data
676
+ retcode = pty.spawn([exe, *cmdlist], read)
677
+ else:
678
+ with open(stdout_file, 'w') as stdout_writer, \
679
+ open(stdout_file, 'r', errors='replace_with_warning') as stdout_reader, \
680
+ open(stderr_file, 'w') as stderr_writer, \
681
+ open(stderr_file, 'r', errors='replace_with_warning') as stderr_reader:
682
+ # if STDOUT and STDERR are to be redirected to the same file,
683
+ # use a single writer
684
+ if stderr_file == stdout_file:
685
+ stderr_writer.close()
686
+ stderr_reader.close()
687
+ stderr_reader = None
688
+ stderr_writer = subprocess.STDOUT
689
+
690
+ preexec_fn = None
691
+ if nice is not None and hasattr(os, 'nice'):
692
+ def set_task_nice():
693
+ os.nice(nice)
694
+ preexec_fn = set_task_nice
695
+
696
+ try:
697
+ proc = subprocess.Popen([exe, *cmdlist],
698
+ stdin=subprocess.DEVNULL,
699
+ stdout=stdout_writer,
700
+ stderr=stderr_writer,
701
+ preexec_fn=preexec_fn)
702
+ except Exception as e:
703
+ raise TaskError(f"Unable to start {exe}: {str(e)}")
704
+
705
+ # How long to wait for proc to quit on ctrl-c before force
706
+ # terminating.
707
+ POLL_INTERVAL = 0.1
708
+ MEMORY_WARN_LIMIT = 90
709
+ try:
710
+ while proc.poll() is None:
711
+ # Gather subprocess memory usage.
712
+ try:
713
+ pproc = psutil.Process(proc.pid)
714
+ proc_mem_bytes = pproc.memory_full_info().uss
715
+ for child in pproc.children(recursive=True):
716
+ proc_mem_bytes += child.memory_full_info().uss
717
+ max_mem_bytes = max(max_mem_bytes, proc_mem_bytes)
718
+
719
+ memory_usage = psutil.virtual_memory()
720
+ if memory_usage.percent > MEMORY_WARN_LIMIT:
721
+ self.__logger.warning(
722
+ 'Current system memory usage is '
723
+ f'{memory_usage.percent:.1f}%')
724
+
725
+ # increase limit warning
726
+ MEMORY_WARN_LIMIT = int(memory_usage.percent + 1)
727
+ except psutil.Error:
728
+ # Process may have already terminated or been killed.
729
+ # Retain existing memory usage statistics in this case.
730
+ pass
731
+ except PermissionError:
732
+ # OS is preventing access to this information so it cannot
733
+ # be collected
734
+ pass
735
+
736
+ # Loop until process terminates
737
+ read_stdio(stdout_reader, stderr_reader)
738
+
739
+ duration = time.time() - cpu_start
740
+ if timeout is not None and duration > timeout:
741
+ raise TaskTimeout(timeout=duration)
742
+
743
+ time.sleep(POLL_INTERVAL)
744
+ except KeyboardInterrupt:
745
+ self.__logger.info("Received ctrl-c.")
746
+ self.__terminate_exe(proc)
747
+ raise TaskError
748
+ except TaskTimeout as e:
749
+ self.__logger.error(f'Task timed out after {e.timeout:.1f} seconds')
750
+ self.__terminate_exe(proc)
751
+ raise e from None
752
+
753
+ # Read the remaining io
754
+ read_stdio(stdout_reader, stderr_reader)
755
+
756
+ retcode = proc.returncode
757
+
758
+ # Record record information
759
+ self.schema("record").record_tool(
760
+ self.__step, self.__index,
761
+ retcode, RecordTool.EXITCODE)
762
+
763
+ # Capture runtime metrics
764
+ self.schema("metric").record(
765
+ self.__step, self.__index,
766
+ 'exetime', time.time() - cpu_start, unit='s')
767
+ self.schema("metric").record(
768
+ self.__step, self.__index,
769
+ 'memory', max_mem_bytes, unit='B')
770
+
771
+ return retcode
772
+
773
+ def __getstate__(self):
774
+ state = self.__dict__.copy()
775
+
776
+ # Remove runtime information
777
+ for key in list(state.keys()):
778
+ if key.startswith("_ToolSchema__"):
779
+ del state[key]
780
+
781
+ return state
782
+
783
+ def __setstate__(self, state):
784
+ self.__dict__ = state
785
+
786
+ # Reinit runtime information
787
+ self.set_runtime(None)
788
+
789
+ def get_output_files(self):
790
+ return set(self.get("task", self.__task, "output", step=self.__step, index=self.__index))
791
+
792
+ ###############################################################
793
+ def parse_version(self, stdout):
794
+ raise NotImplementedError("must be implemented by the implementation class")
795
+
796
+ def normalize_version(self, version):
797
+ return version
798
+
799
+ def setup(self):
800
+ pass
801
+
802
+ def select_input_nodes(self):
803
+ flow = self.schema("flow")
804
+ runtime = RuntimeFlowgraph(
805
+ flow,
806
+ from_steps=set([step for step, _ in flow.get_entry_nodes()]),
807
+ prune_nodes=self.__chip.get('option', 'prune'))
808
+
809
+ return runtime.get_node_inputs(self.__step, self.__index, record=self.schema("record"))
810
+
811
+ def pre_process(self):
812
+ pass
813
+
814
+ def runtime_options(self):
815
+ return []
816
+
817
+ def run(self):
818
+ raise NotImplementedError("must be implemented by the implementation class")
819
+
820
+ def post_process(self):
821
+ pass
822
+
823
+
824
+ ###########################################################################
825
+ # Migration helper
826
+ ###########################################################################
827
+ class ToolSchemaTmp(ToolSchema):
828
+ def __module_func(self, name, modules):
829
+ for module in modules:
830
+ method = getattr(module, name, None)
831
+ if method:
832
+ return method
833
+ return None
834
+
835
+ def __tool_task_modules(self):
836
+ step, index = self.node()
837
+ flow = self._ToolSchema__chip.get('option', 'flow')
838
+ return \
839
+ self._ToolSchema__chip._get_tool_module(step, index, flow=flow), \
840
+ self._ToolSchema__chip._get_task_module(step, index, flow=flow)
841
+
842
+ def get_output_files(self):
843
+ _, task = self.__tool_task_modules()
844
+ method = self.__module_func("_gather_outputs", [task])
845
+ if method:
846
+ return method(self._ToolSchema__chip, *self.node())
847
+ return ToolSchema.get_output_files(self)
848
+
849
+ def parse_version(self, stdout):
850
+ tool, _ = self.__tool_task_modules()
851
+ method = self.__module_func("parse_version", [tool])
852
+ if method:
853
+ return method(stdout)
854
+ return ToolSchema.parse_version(self, stdout)
855
+
856
+ def normalize_version(self, version):
857
+ tool, _ = self.__tool_task_modules()
858
+ method = self.__module_func("normalize_version", [tool])
859
+ if method:
860
+ return method(version)
861
+ return ToolSchema.normalize_version(self, version)
862
+
863
+ def setup(self):
864
+ _, task = self.__tool_task_modules()
865
+ method = self.__module_func("setup", [task])
866
+ if method:
867
+ return method(self._ToolSchema__chip)
868
+ return ToolSchema.setup(self)
869
+
870
+ def select_input_nodes(self):
871
+ _, task = self.__tool_task_modules()
872
+ method = self.__module_func("_select_inputs", [task])
873
+ if method:
874
+ return method(self._ToolSchema__chip, *self.node())
875
+ return ToolSchema.select_input_nodes(self)
876
+
877
+ def pre_process(self):
878
+ _, task = self.__tool_task_modules()
879
+ method = self.__module_func("pre_process", [task])
880
+ if method:
881
+ return method(self._ToolSchema__chip)
882
+ return ToolSchema.pre_process(self)
883
+
884
+ def runtime_options(self):
885
+ tool, task = self.__tool_task_modules()
886
+ method = self.__module_func("runtime_options", [task, tool])
887
+ if method:
888
+ return method(self._ToolSchema__chip)
889
+ return ToolSchema.runtime_options(self)
890
+
891
+ def run(self):
892
+ _, task = self.__tool_task_modules()
893
+ method = self.__module_func("run", [task])
894
+ if method:
895
+ # Handle logger stdout suppression if quiet
896
+ step, index = self.node()
897
+ stdout_handler_level = self._ToolSchema__chip.logger._console.level
898
+ if self._ToolSchema__chip.get('option', 'quiet', step=step, index=index):
899
+ self._ToolSchema__chip.logger._console.setLevel(logging.CRITICAL)
900
+
901
+ retcode = method(self._ToolSchema__chip)
902
+
903
+ self._ToolSchema__chip.logger._console.setLevel(stdout_handler_level)
904
+
905
+ return retcode
906
+ return ToolSchema.run(self)
907
+
908
+ def post_process(self):
909
+ _, task = self.__tool_task_modules()
910
+ method = self.__module_func("post_process", [task])
911
+ if method:
912
+ return method(self._ToolSchema__chip)
913
+ return ToolSchema.post_process(self)
914
+
915
+
916
+ ###########################################################################
917
+ # Tool Setup
918
+ ###########################################################################
919
+ def schema_tool(schema):
920
+ schema = EditableSchema(schema)
921
+
922
+ schema.insert(
923
+ 'exe',
924
+ Parameter(
925
+ 'str',
926
+ scope=Scope.GLOBAL,
927
+ shorthelp="Tool: executable name",
928
+ switch="-tool_exe 'tool <str>'",
929
+ example=["cli: -tool_exe 'openroad openroad'",
930
+ "api: chip.set('tool', 'openroad', 'exe', 'openroad')"],
931
+ help=trim("""Tool executable name.""")))
932
+
933
+ schema.insert(
934
+ 'sbom', 'default',
935
+ Parameter(
936
+ '[file]',
937
+ scope=Scope.GLOBAL,
938
+ pernode=PerNode.OPTIONAL,
939
+ shorthelp="Tool: software BOM",
940
+ switch="-tool_sbom 'tool version <file>'",
941
+ example=[
942
+ "cli: -tool_sbom 'yosys 1.0.1 ys_sbom.json'",
943
+ "api: chip.set('tool', 'yosys', 'sbom', '1.0', 'ys_sbom.json')"],
944
+ help=trim("""
945
+ Paths to software bill of material (SBOM) document file of the tool
946
+ specified on a per version basis. The SBOM includes critical
947
+ package information about the tool including the list of included
948
+ components, licenses, and copyright. The SBOM file is generally
949
+ provided as in a a standardized open data format such as SPDX.""")))
950
+
951
+ schema.insert(
952
+ 'path',
953
+ Parameter(
954
+ 'dir',
955
+ scope=Scope.GLOBAL,
956
+ pernode=PerNode.OPTIONAL,
957
+ shorthelp="Tool: executable path",
958
+ switch="-tool_path 'tool <dir>'",
959
+ example=[
960
+ "cli: -tool_path 'openroad /usr/local/bin'",
961
+ "api: chip.set('tool', 'openroad', 'path', '/usr/local/bin')"],
962
+ help=trim("""
963
+ File system path to tool executable. The path is prepended to the
964
+ system PATH environment variable for batch and interactive runs. The
965
+ path parameter can be left blank if the :keypath:`tool,<tool>,exe` is already in the
966
+ environment search path.""")))
967
+
968
+ schema.insert(
969
+ 'vswitch',
970
+ Parameter(
971
+ '[str]',
972
+ scope=Scope.GLOBAL,
973
+ shorthelp="Tool: executable version switch",
974
+ switch="-tool_vswitch 'tool <str>'",
975
+ example=["cli: -tool_vswitch 'openroad -version'",
976
+ "api: chip.set('tool', 'openroad', 'vswitch', '-version')"],
977
+ help=trim("""
978
+ Command line switch to use with executable used to print out
979
+ the version number. Common switches include ``-v``, ``-version``,
980
+ ``--version``. Some tools may require extra flags to run in batch mode.""")))
981
+
982
+ schema.insert(
983
+ 'vendor',
984
+ Parameter(
985
+ 'str',
986
+ scope=Scope.GLOBAL,
987
+ shorthelp="Tool: vendor",
988
+ switch="-tool_vendor 'tool <str>'",
989
+ example=["cli: -tool_vendor 'yosys yosys'",
990
+ "api: chip.set('tool', 'yosys', 'vendor', 'yosys')"],
991
+ help=trim("""
992
+ Name of the tool vendor. Parameter can be used to set vendor
993
+ specific technology variables in the PDK and libraries. For
994
+ open source projects, the project name should be used in
995
+ place of vendor.""")))
996
+
997
+ schema.insert(
998
+ 'version',
999
+ Parameter(
1000
+ '[str]',
1001
+ scope=Scope.GLOBAL,
1002
+ pernode=PerNode.OPTIONAL,
1003
+ shorthelp="Tool: version",
1004
+ switch="-tool_version 'tool <str>'",
1005
+ example=["cli: -tool_version 'openroad >=v2.0'",
1006
+ "api: chip.set('tool', 'openroad', 'version', '>=v2.0')"],
1007
+ help=trim("""
1008
+ List of acceptable versions of the tool executable to be used. Each
1009
+ entry in this list must be a version specifier as described by Python
1010
+ `PEP-440 <https://peps.python.org/pep-0440/#version-specifiers>`_.
1011
+ During task execution, the tool is called with the 'vswitch' to
1012
+ check the runtime executable version. If the version of the system
1013
+ executable is not allowed by any of the specifiers in 'version',
1014
+ then the job is halted pre-execution. For backwards compatibility,
1015
+ entries that do not conform to the standard will be interpreted as a
1016
+ version with an '==' specifier. This check can be disabled by
1017
+ setting :keypath:`option,novercheck` to True.""")))
1018
+
1019
+ schema.insert(
1020
+ 'format',
1021
+ Parameter(
1022
+ '<json,tcl,yaml>',
1023
+ scope=Scope.GLOBAL,
1024
+ shorthelp="Tool: file format",
1025
+ switch="-tool_format 'tool <str>'",
1026
+ example=["cli: -tool_format 'yosys tcl'",
1027
+ "api: chip.set('tool', 'yosys', 'format', 'tcl')"],
1028
+ help=trim("""
1029
+ File format for tool manifest handoff.""")))
1030
+
1031
+ schema.insert(
1032
+ 'licenseserver', 'default',
1033
+ Parameter(
1034
+ '[str]',
1035
+ scope=Scope.GLOBAL,
1036
+ pernode=PerNode.OPTIONAL,
1037
+ shorthelp="Tool: license servers",
1038
+ switch="-tool_licenseserver 'name key <str>'",
1039
+ example=[
1040
+ "cli: -tool_licenseserver 'atask ACME_LICENSE 1700@server'",
1041
+ "api: chip.set('tool', 'acme', 'licenseserver', 'ACME_LICENSE', '1700@server')"],
1042
+ help=trim("""
1043
+ Defines a set of tool specific environment variables used by the executable
1044
+ that depend on license key servers to control access. For multiple servers,
1045
+ separate each server by a 'colon'. The named license variable are read at
1046
+ runtime (:meth:`.run()`) and the environment variables are set.
1047
+ """)))
1048
+
1049
+
1050
+ def schema_task(schema):
1051
+ schema = EditableSchema(schema)
1052
+
1053
+ schema.insert(
1054
+ 'warningoff',
1055
+ Parameter(
1056
+ '[str]',
1057
+ scope=Scope.JOB,
1058
+ pernode=PerNode.OPTIONAL,
1059
+ shorthelp="Task: warning filter",
1060
+ switch="-tool_task_warningoff 'tool task <str>'",
1061
+ example=[
1062
+ "cli: -tool_task_warningoff 'verilator lint COMBDLY'",
1063
+ "api: chip.set('tool', 'verilator', 'task', 'lint', 'warningoff', 'COMBDLY')"],
1064
+ help=trim("""
1065
+ A list of tool warnings for which printing should be suppressed.
1066
+ Generally this is done on a per design basis after review has
1067
+ determined that warning can be safely ignored The code for turning
1068
+ off warnings can be found in the specific task reference manual.
1069
+ """)))
1070
+
1071
+ schema.insert(
1072
+ 'regex', 'default',
1073
+ Parameter(
1074
+ '[str]',
1075
+ scope=Scope.JOB,
1076
+ pernode=PerNode.OPTIONAL,
1077
+ shorthelp="Task: regex filter",
1078
+ switch="-tool_task_regex 'tool task suffix <str>'",
1079
+ example=[
1080
+ "cli: -tool_task_regex 'openroad place errors \"'-v ERROR'\"'",
1081
+ "api: chip.set('tool', 'openroad', 'task', 'place', 'regex', 'errors', "
1082
+ "'-v ERROR')"],
1083
+ help=trim("""
1084
+ A list of piped together grep commands. Each entry represents a set
1085
+ of command line arguments for grep including the regex pattern to
1086
+ match. Starting with the first list entry, each grep output is piped
1087
+ into the following grep command in the list. Supported grep options
1088
+ include ``-v`` and ``-e``. Patterns starting with "-" should be
1089
+ directly preceded by the ``-e`` option. The following example
1090
+ illustrates the concept.
1091
+
1092
+ UNIX grep:
1093
+
1094
+ .. code-block:: bash
1095
+
1096
+ $ grep WARNING place.log | grep -v "bbox" > place.warnings
1097
+
1098
+ SiliconCompiler::
1099
+
1100
+ chip.set('task', 'openroad', 'regex', 'place', '0', 'warnings',
1101
+ ["WARNING", "-v bbox"])
1102
+
1103
+ The "errors" and "warnings" suffixes are special cases. When set,
1104
+ the number of matches found for these regexes will be added to the
1105
+ errors and warnings metrics for the task, respectively. This will
1106
+ also cause the logfile to be added to the :keypath:`tool, <tool>,
1107
+ task, <task>, report` parameter for those metrics, if not already present.""")))
1108
+
1109
+ # Configuration: cli-option, tcl var, env var, file
1110
+ schema.insert(
1111
+ 'option',
1112
+ Parameter(
1113
+ '[str]',
1114
+ scope=Scope.JOB,
1115
+ pernode=PerNode.OPTIONAL,
1116
+ shorthelp="Task: executable options",
1117
+ switch="-tool_task_option 'tool task <str>'",
1118
+ example=[
1119
+ "cli: -tool_task_option 'openroad cts -no_init'",
1120
+ "api: chip.set('tool', 'openroad', 'task', 'cts', 'option', '-no_init')"],
1121
+ help=trim("""
1122
+ List of command line options for the task executable, specified on
1123
+ a per task and per step basis. Options must not include spaces.
1124
+ For multiple argument options, each option is a separate list element.
1125
+ """)))
1126
+
1127
+ schema.insert(
1128
+ 'var', 'default',
1129
+ Parameter(
1130
+ '[str]',
1131
+ scope=Scope.JOB,
1132
+ pernode=PerNode.OPTIONAL,
1133
+ shorthelp="Task: script variables",
1134
+ switch="-tool_task_var 'tool task key <str>'",
1135
+ example=[
1136
+ "cli: -tool_task_var 'openroad cts myvar 42'",
1137
+ "api: chip.set('tool', 'openroad', 'task', 'cts', 'var', 'myvar', '42')"],
1138
+ help=trim("""
1139
+ Task script variables specified as key value pairs. Variable
1140
+ names and value types must match the name and type of task and reference
1141
+ script consuming the variable.""")))
1142
+
1143
+ schema.insert(
1144
+ 'env', 'default',
1145
+ Parameter(
1146
+ 'str',
1147
+ scope=Scope.JOB,
1148
+ pernode=PerNode.OPTIONAL,
1149
+ shorthelp="Task: environment variables",
1150
+ switch="-tool_task_env 'tool task env <str>'",
1151
+ example=[
1152
+ "cli: -tool_task_env 'openroad cts MYVAR 42'",
1153
+ "api: chip.set('tool', 'openroad', 'task', 'cts', 'env', 'MYVAR', '42')"],
1154
+ help=trim("""
1155
+ Environment variables to set for individual tasks. Keys and values
1156
+ should be set in accordance with the task's documentation. Most
1157
+ tasks do not require extra environment variables to function.""")))
1158
+
1159
+ schema.insert(
1160
+ 'file', 'default', Parameter(
1161
+ '[file]',
1162
+ scope=Scope.JOB,
1163
+ pernode=PerNode.OPTIONAL,
1164
+ copy=True,
1165
+ shorthelp="Task: custom setup files",
1166
+ switch="-tool_task_file 'tool task key <file>'",
1167
+ example=[
1168
+ "cli: -tool_task_file 'openroad floorplan macroplace macroplace.tcl'",
1169
+ "api: chip.set('tool', 'openroad', 'task', 'floorplan', 'file', 'macroplace', "
1170
+ "'macroplace.tcl')"],
1171
+ help=trim("""
1172
+ Paths to user supplied files mapped to keys. Keys and filetypes must
1173
+ match what's expected by the task/reference script consuming the
1174
+ file.
1175
+ """)))
1176
+
1177
+ schema.insert(
1178
+ 'dir', 'default',
1179
+ Parameter(
1180
+ '[dir]',
1181
+ scope=Scope.JOB,
1182
+ pernode=PerNode.OPTIONAL,
1183
+ copy=True,
1184
+ shorthelp="Task: custom setup directories",
1185
+ switch="-tool_task_dir 'tool task key <dir>'",
1186
+ example=[
1187
+ "cli: -tool_task_dir 'verilator compile cincludes include'",
1188
+ "api: chip.set('tool', 'verilator', 'task', 'compile', 'dir', 'cincludes', "
1189
+ "'include')"],
1190
+ help=trim("""
1191
+ Paths to user supplied directories mapped to keys. Keys must match
1192
+ what's expected by the task/reference script consuming the
1193
+ directory.
1194
+ """)))
1195
+
1196
+ # Definitions of inputs, outputs, requirements
1197
+ schema.insert(
1198
+ 'input',
1199
+ Parameter(
1200
+ '[file]',
1201
+ scope=Scope.JOB,
1202
+ pernode=PerNode.REQUIRED,
1203
+ shorthelp="Task: input files",
1204
+ switch="-tool_task_input 'tool task <file>'",
1205
+ example=[
1206
+ "cli: -tool_task_input 'openroad place \"place 0 oh_add.def\"'",
1207
+ "api: chip.set('tool', 'openroad', 'task', 'place', 'input', 'oh_add.def', "
1208
+ "step='place', index='0')"],
1209
+ help=trim("""
1210
+ List of data files to be copied from previous flowgraph steps 'output'
1211
+ directory. The list of steps to copy files from is defined by the
1212
+ list defined by the dictionary key :keypath:`flowgraph,<flow>,<step>,<index>,input`.
1213
+ All files must be available for flow to continue. If a file
1214
+ is missing, the program exists on an error.""")))
1215
+
1216
+ schema.insert(
1217
+ 'output',
1218
+ Parameter(
1219
+ '[file]',
1220
+ scope=Scope.JOB,
1221
+ pernode=PerNode.REQUIRED,
1222
+ shorthelp="Task: output files",
1223
+ switch="-tool_task_output 'tool task <file>'",
1224
+ example=[
1225
+ "cli: -tool_task_output 'openroad place \"place 0 oh_add.def\"'",
1226
+ "api: chip.set('tool', 'openroad', 'task', 'place', 'output', 'oh_add.def', "
1227
+ "step='place', index='0')"],
1228
+ help=trim("""
1229
+ List of data files written to the 'output' directory of the
1230
+ tool/task/step/index used in the keypath. All files must be available
1231
+ for flow to continue. If a file is missing, the program exists on an error.""")))
1232
+
1233
+ dest_enum = ['log', 'output', 'none']
1234
+ schema.insert(
1235
+ 'stdout', 'destination',
1236
+ Parameter(
1237
+ f'<{",".join(dest_enum)}>',
1238
+ defvalue='log',
1239
+ scope=Scope.JOB,
1240
+ pernode=PerNode.OPTIONAL,
1241
+ shorthelp="Task: destination for stdout",
1242
+ switch="-tool_task_stdout_destination 'tool task <str>'",
1243
+ example=["cli: -tool_task_stdout_destination 'ghdl import log'",
1244
+ "api: chip.set('tool', 'ghdl', 'task', 'import', 'stdout', 'destination', "
1245
+ "'log')"],
1246
+ help=trim("""
1247
+ Defines where to direct the output generated over stdout.
1248
+ Supported options are:
1249
+ none: the stream generated to STDOUT is ignored.
1250
+ log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
1251
+ it is additionally dumped to the display.
1252
+ output: the generated stream is stored in outputs/<design>.<suffix>.""")))
1253
+
1254
+ schema.insert(
1255
+ 'stdout', 'suffix',
1256
+ Parameter(
1257
+ 'str',
1258
+ defvalue='log',
1259
+ scope=Scope.JOB,
1260
+ pernode=PerNode.OPTIONAL,
1261
+ shorthelp="Task: file suffix for redirected stdout",
1262
+ switch="-tool_task_stdout_suffix 'tool task <str>'",
1263
+ example=["cli: -tool_task_stdout_suffix 'ghdl import log'",
1264
+ "api: chip.set('tool', ghdl', 'task', 'import', 'stdout', 'suffix', 'log')"],
1265
+ help=trim("""
1266
+ Specifies the file extension for the content redirected from stdout.""")))
1267
+
1268
+ schema.insert(
1269
+ 'stderr', 'destination',
1270
+ Parameter(
1271
+ f'<{",".join(dest_enum)}>',
1272
+ defvalue='log',
1273
+ scope=Scope.JOB,
1274
+ pernode=PerNode.OPTIONAL,
1275
+ shorthelp="Task: destination for stderr",
1276
+ switch="-tool_task_stderr_destination 'tool task <str>'",
1277
+ example=["cli: -tool_task_stderr_destination 'ghdl import log'",
1278
+ "api: chip.set('tool', ghdl', 'task', 'import', 'stderr', 'destination', "
1279
+ "'log')"],
1280
+ help=trim("""
1281
+ Defines where to direct the output generated over stderr.
1282
+ Supported options are:
1283
+ none: the stream generated to STDERR is ignored
1284
+ log: the generated stream is stored in <step>.<suffix>; if not in quiet mode,
1285
+ it is additionally dumped to the display.
1286
+ output: the generated stream is stored in outputs/<design>.<suffix>""")))
1287
+
1288
+ schema.insert(
1289
+ 'stderr', 'suffix',
1290
+ Parameter(
1291
+ 'str',
1292
+ defvalue='log',
1293
+ scope=Scope.JOB,
1294
+ pernode=PerNode.OPTIONAL,
1295
+ shorthelp="Task: file suffix for redirected stderr",
1296
+ switch="-tool_task_stderr_suffix 'tool task <str>'",
1297
+ example=["cli: -tool_task_stderr_suffix 'ghdl import log'",
1298
+ "api: chip.set('tool', 'ghdl', 'task', 'import', 'stderr', 'suffix', 'log')"],
1299
+ help=trim("""
1300
+ Specifies the file extension for the content redirected from stderr.""")))
1301
+
1302
+ schema.insert(
1303
+ 'require',
1304
+ Parameter(
1305
+ '[str]',
1306
+ scope=Scope.JOB,
1307
+ pernode=PerNode.OPTIONAL,
1308
+ shorthelp="Task: parameter requirements",
1309
+ switch="-tool_task_require 'tool task <str>'",
1310
+ example=[
1311
+ "cli: -tool_task_require 'openroad cts design'",
1312
+ "api: chip.set('tool', 'openroad', 'task', 'cts', 'require', 'design')"],
1313
+ help=trim("""
1314
+ List of keypaths to required task parameters. The list is used
1315
+ by :meth:`.check_manifest()` to verify that all parameters have been set up before
1316
+ step execution begins.""")))
1317
+
1318
+ schema.insert(
1319
+ 'report', 'default',
1320
+ Parameter(
1321
+ '[file]',
1322
+ scope=Scope.JOB,
1323
+ pernode=PerNode.REQUIRED,
1324
+ shorthelp="Task: metric report files",
1325
+ switch="-tool_task_report 'tool task metric <file>'",
1326
+ example=[
1327
+ "cli: -tool_task_report 'openroad place holdtns \"place 0 place.log\"'",
1328
+ "api: chip.set('tool', 'openroad', 'task', 'place', 'report', 'holdtns', "
1329
+ "'place.log', step='place', index='0')"],
1330
+ help=trim("""
1331
+ List of report files associated with a specific 'metric'. The file path
1332
+ specified is relative to the run directory of the current task.""")))
1333
+
1334
+ schema.insert(
1335
+ 'refdir',
1336
+ Parameter(
1337
+ '[dir]',
1338
+ scope=Scope.JOB,
1339
+ pernode=PerNode.OPTIONAL,
1340
+ shorthelp="Task: script directory",
1341
+ switch="-tool_task_refdir 'tool task <dir>'",
1342
+ example=[
1343
+ "cli: -tool_task_refdir 'yosys syn ./myref'",
1344
+ "api: chip.set('tool', 'yosys', 'task', 'syn_asic', 'refdir', './myref')"],
1345
+ help=trim("""
1346
+ Path to directories containing reference flow scripts, specified
1347
+ on a per step and index basis.""")))
1348
+
1349
+ schema.insert(
1350
+ 'script',
1351
+ Parameter(
1352
+ '[file]',
1353
+ scope=Scope.JOB,
1354
+ pernode=PerNode.OPTIONAL,
1355
+ shorthelp="Task: entry script",
1356
+ switch="-tool_task_script 'tool task <file>'",
1357
+ example=[
1358
+ "cli: -tool_task_script 'yosys syn syn.tcl'",
1359
+ "api: chip.set('tool', 'yosys', 'task', 'syn_asic', 'script', 'syn.tcl')"],
1360
+ help=trim("""
1361
+ Path to the entry script called by the executable specified
1362
+ on a per task and per step basis.""")))
1363
+
1364
+ schema.insert(
1365
+ 'prescript',
1366
+ Parameter(
1367
+ '[file]',
1368
+ scope=Scope.JOB,
1369
+ pernode=PerNode.OPTIONAL,
1370
+ copy=True,
1371
+ shorthelp="Task: pre-step script",
1372
+ switch="-tool_task_prescript 'tool task <file>'",
1373
+ example=[
1374
+ "cli: -tool_task_prescript 'yosys syn syn_pre.tcl'",
1375
+ "api: chip.set('tool', 'yosys', 'task', 'syn_asic', 'prescript', 'syn_pre.tcl')"],
1376
+ help=trim("""
1377
+ Path to a user supplied script to execute after reading in the design
1378
+ but before the main execution stage of the step. Exact entry point
1379
+ depends on the step and main script being executed. An example
1380
+ of a prescript entry point would be immediately before global
1381
+ placement.""")))
1382
+
1383
+ schema.insert(
1384
+ 'postscript',
1385
+ Parameter(
1386
+ '[file]',
1387
+ scope=Scope.JOB,
1388
+ pernode=PerNode.OPTIONAL,
1389
+ copy=True,
1390
+ shorthelp="Task: post-step script",
1391
+ switch="-tool_task_postscript 'tool task <file>'",
1392
+ example=[
1393
+ "cli: -tool_task_postscript 'yosys syn syn_post.tcl'",
1394
+ "api: chip.set('tool', 'yosys', 'task', 'syn_asic', 'postscript', 'syn_post.tcl')"],
1395
+ help=trim("""
1396
+ Path to a user supplied script to execute after the main execution
1397
+ stage of the step but before the design is saved.
1398
+ Exact entry point depends on the step and main script being
1399
+ executed. An example of a postscript entry point would be immediately
1400
+ after global placement.""")))
1401
+
1402
+ schema.insert(
1403
+ 'threads',
1404
+ Parameter(
1405
+ 'int',
1406
+ scope=Scope.JOB,
1407
+ pernode=PerNode.OPTIONAL,
1408
+ shorthelp="Task: thread parallelism",
1409
+ switch="-tool_task_threads 'tool task <int>'",
1410
+ example=["cli: -tool_task_threads 'magic drc 64'",
1411
+ "api: chip.set('tool', 'magic', 'task', 'drc', 'threads', '64')"],
1412
+ help=trim("""
1413
+ Thread parallelism to use for execution specified on a per task and per
1414
+ step basis. If not specified, SC queries the operating system and sets
1415
+ the threads based on the maximum thread count supported by the
1416
+ hardware.""")))