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
@@ -1,60 +1,22 @@
1
- import contextlib
2
- import distro
3
- import getpass
4
- import multiprocessing
5
1
  import logging
6
2
  import os
7
- import platform
8
- import psutil
9
- import socket
10
3
  import re
11
- import shlex
12
4
  import shutil
13
- import subprocess
14
5
  import sys
15
- import time
16
- import packaging.version
17
- import packaging.specifiers
18
- from io import StringIO
19
- import traceback
20
- from datetime import datetime
21
- from logging.handlers import QueueHandler, QueueListener
6
+ from logging.handlers import QueueHandler
22
7
  from siliconcompiler import sc_open
23
8
  from siliconcompiler import utils
24
- from siliconcompiler import _metadata
25
9
  from siliconcompiler.remote import Client
26
- from siliconcompiler.schema import Schema
27
- from siliconcompiler.scheduler import slurm
28
- from siliconcompiler.scheduler import docker_runner
10
+ from siliconcompiler import Schema
11
+ from siliconcompiler.schema import JournalingSchema
12
+ from siliconcompiler.record import RecordTime, RecordTool
29
13
  from siliconcompiler import NodeStatus, SiliconCompilerError
30
- from siliconcompiler.flowgraph import _get_flowgraph_nodes, _get_flowgraph_execution_order, \
31
- _get_pruned_node_inputs, _get_flowgraph_entry_nodes, \
32
- _unreachable_steps_to_execute, _nodes_to_execute, \
33
- get_nodes_from, nodes_to_execute, _check_flowgraph
34
- from siliconcompiler.utils.logging import SCBlankLoggerFormatter
35
14
  from siliconcompiler.tools._common import input_file_node_name
36
15
  import lambdapdk
37
16
  from siliconcompiler.tools._common import get_tool_task, record_metric
38
17
  from siliconcompiler.scheduler import send_messages
39
-
40
- try:
41
- import resource
42
- except ModuleNotFoundError:
43
- resource = None
44
-
45
-
46
- # callback hooks to help custom runners track progress
47
- _callback_funcs = {}
48
-
49
-
50
- def register_callback(hook, func):
51
- _callback_funcs[hook] = func
52
-
53
-
54
- def _get_callback(hook):
55
- if hook in _callback_funcs:
56
- return _callback_funcs[hook]
57
- return None
18
+ from siliconcompiler.flowgraph import RuntimeFlowgraph
19
+ from siliconcompiler.scheduler.taskscheduler import TaskScheduler
58
20
 
59
21
 
60
22
  # Max lines to print from failed node log
@@ -93,15 +55,31 @@ def run(chip):
93
55
 
94
56
  # Check if flowgraph is complete and valid
95
57
  flow = chip.get('option', 'flow')
96
- if not _check_flowgraph(chip, flow=flow):
58
+ if not chip.schema.get("flowgraph", flow, field="schema").validate(logger=chip.logger):
59
+ raise SiliconCompilerError(
60
+ f"{flow} flowgraph contains errors and cannot be run.",
61
+ chip=chip)
62
+ if not RuntimeFlowgraph.validate(
63
+ chip.schema.get("flowgraph", flow, field="schema"),
64
+ from_steps=chip.get('option', 'from'),
65
+ to_steps=chip.get('option', 'to'),
66
+ prune_nodes=chip.get('option', 'prune'),
67
+ logger=chip.logger):
97
68
  raise SiliconCompilerError(
98
69
  f"{flow} flowgraph contains errors and cannot be run.",
99
70
  chip=chip)
100
71
 
101
72
  copy_old_run_dir(chip, org_jobname)
102
73
  clean_build_dir(chip)
103
- _reset_flow_nodes(chip, flow, nodes_to_execute(chip, flow))
104
- __record_packages(chip)
74
+
75
+ runtime = RuntimeFlowgraph(
76
+ chip.schema.get("flowgraph", flow, field='schema'),
77
+ from_steps=chip.get('option', 'from'),
78
+ to_steps=chip.get('option', 'to'),
79
+ prune_nodes=chip.get('option', 'prune'))
80
+
81
+ _reset_flow_nodes(chip, flow, runtime.get_nodes())
82
+ chip.schema.get("record", field='schema').record_python_packages()
105
83
 
106
84
  if chip.get('option', 'remote'):
107
85
  client = Client(chip)
@@ -175,20 +153,19 @@ def _local_process(chip, flow):
175
153
  extra_setup_nodes = {}
176
154
 
177
155
  if chip.get('option', 'clean') or not chip.get('option', 'from'):
178
- load_nodes = _get_flowgraph_nodes(chip, flow)
156
+ load_nodes = list(chip.schema.get("flowgraph", flow, field="schema").get_nodes())
179
157
  else:
180
158
  for step in chip.get('option', 'from'):
181
159
  from_nodes.extend(
182
160
  [(step, index) for index in chip.getkeys('flowgraph', flow, step)])
183
161
 
184
- load_nodes = _nodes_to_execute(
185
- chip,
186
- flow,
187
- _get_flowgraph_entry_nodes(chip, flow),
188
- from_nodes,
189
- chip.get('option', 'prune'))
162
+ runtime = RuntimeFlowgraph(
163
+ chip.schema.get("flowgraph", flow, field="schema"),
164
+ to_steps=chip.get('option', 'from'),
165
+ prune_nodes=chip.get('option', 'prune'))
166
+ load_nodes = list(runtime.get_nodes())
190
167
 
191
- for node_level in _get_flowgraph_execution_order(chip, flow):
168
+ for node_level in chip.schema.get("flowgraph", flow, field="schema").get_execution_order():
192
169
  for step, index in node_level:
193
170
  if (step, index) not in load_nodes:
194
171
  continue
@@ -201,14 +178,22 @@ def _local_process(chip, flow):
201
178
  if os.path.exists(manifest):
202
179
  # ensure we setup these nodes again
203
180
  try:
204
- extra_setup_nodes[(step, index)] = Schema(manifest=manifest, logger=chip.logger)
181
+ journal = JournalingSchema(Schema())
182
+ journal.read_manifest(manifest)
183
+ extra_setup_nodes[(step, index)] = journal
205
184
  except Exception:
206
185
  pass
207
186
 
187
+ runtimeflow = RuntimeFlowgraph(
188
+ chip.schema.get("flowgraph", flow, field="schema"),
189
+ from_steps=chip.get('option', 'from'),
190
+ to_steps=chip.get('option', 'to'),
191
+ prune_nodes=chip.get('option', 'prune'))
192
+
208
193
  # Setup tools for all nodes to run.
209
- nodes = nodes_to_execute(chip, flow)
194
+ nodes = list(runtimeflow.get_nodes())
210
195
  all_setup_nodes = nodes + load_nodes + list(extra_setup_nodes.keys())
211
- for layer_nodes in _get_flowgraph_execution_order(chip, flow):
196
+ for layer_nodes in chip.schema.get("flowgraph", flow, field="schema").get_execution_order():
212
197
  for step, index in layer_nodes:
213
198
  if (step, index) in all_setup_nodes:
214
199
  node_kept = _setup_node(chip, step, index)
@@ -222,32 +207,37 @@ def _local_process(chip, flow):
222
207
  except: # noqa E722
223
208
  pass
224
209
  if node_status:
225
- chip.set('record', 'status', node_status, step=step, index=index)
210
+ chip.schema.get("record", field='schema').set('status', node_status,
211
+ step=step, index=index)
226
212
 
227
213
  def mark_pending(step, index):
228
- chip.set('record', 'status', NodeStatus.PENDING, step=step, index=index)
229
- for next_step, next_index in get_nodes_from(chip, flow, [(step, index)]):
214
+ chip.schema.get("record", field='schema').set('status', NodeStatus.PENDING,
215
+ step=step, index=index)
216
+ for next_step, next_index in runtimeflow.get_nodes_starting_at(step, index):
230
217
  if chip.get('record', 'status', step=next_step, index=next_index) == \
231
218
  NodeStatus.SKIPPED:
232
219
  continue
233
220
 
234
221
  # Mark following steps as pending
235
- chip.set('record', 'status', NodeStatus.PENDING, step=next_step, index=next_index)
222
+ chip.schema.get("record", field='schema').set('status', NodeStatus.PENDING,
223
+ step=next_step, index=next_index)
236
224
 
237
225
  # Check if nodes have been modified from previous data
238
- for layer_nodes in _get_flowgraph_execution_order(chip, flow):
226
+ for layer_nodes in chip.schema.get("flowgraph", flow, field="schema").get_execution_order():
239
227
  for step, index in layer_nodes:
240
228
  # Only look at successful nodes
241
229
  if chip.get('record', 'status', step=step, index=index) not in \
242
230
  (NodeStatus.SUCCESS, NodeStatus.SKIPPED):
243
231
  continue
244
232
 
245
- if not check_node_inputs(chip, step, index):
233
+ if (step, index) in runtimeflow.get_nodes() and \
234
+ not check_node_inputs(chip, step, index):
246
235
  # change failing nodes to pending
247
236
  mark_pending(step, index)
248
237
  elif (step, index) in extra_setup_nodes:
249
238
  # import old information
250
- chip.schema._import_journal(extra_setup_nodes[(step, index)])
239
+ JournalingSchema(chip.schema).import_journal(
240
+ schema=extra_setup_nodes[(step, index)])
251
241
 
252
242
  # Ensure pending nodes cause following nodes to be run
253
243
  for step, index in nodes:
@@ -273,40 +263,11 @@ def _local_process(chip, flow):
273
263
  'Implementation errors encountered. See previous errors.',
274
264
  chip=chip)
275
265
 
276
- nodes_to_run = {}
277
- processes = {}
278
- local_processes = []
279
- log_queue = _prepare_nodes(chip, nodes_to_run, processes, local_processes, flow)
280
-
281
- # Handle logs across threads
282
- log_listener = QueueListener(log_queue, chip.logger._console)
283
- chip.logger._console.setFormatter(SCBlankLoggerFormatter())
284
- log_listener.start()
285
-
286
- # Update dashboard before run begins
287
- if chip._dash:
288
- chip._dash.update_manifest()
289
-
290
- try:
291
- _launch_nodes(chip, nodes_to_run, processes, local_processes)
292
- except KeyboardInterrupt:
293
- # exit immediately
294
- log_listener.stop()
295
- sys.exit(0)
296
-
297
- if _get_callback('post_run'):
298
- _get_callback('post_run')(chip)
266
+ task_scheduler = TaskScheduler(chip)
267
+ task_scheduler.run()
299
268
 
300
269
  _check_nodes_status(chip, flow)
301
270
 
302
- # Cleanup logger
303
- log_listener.stop()
304
- chip._init_logger_formats()
305
-
306
-
307
- def __is_posix():
308
- return sys.platform != 'win32'
309
-
310
271
 
311
272
  ###########################################################################
312
273
  def _setup_node(chip, step, index, flow=None):
@@ -321,21 +282,19 @@ def _setup_node(chip, step, index, flow=None):
321
282
  chip.set('arg', 'index', index)
322
283
  tool, task = get_tool_task(chip, step, index, flow=flow)
323
284
 
285
+ task_class = chip.get("tool", tool, field="schema")
286
+ task_class.set_runtime(chip)
287
+
324
288
  # Run node setup.
289
+ chip.logger.info(f'Setting up node {step}{index} with {tool}/{task}')
325
290
  setup_ret = None
326
291
  try:
327
- setup_step = getattr(chip._get_task_module(step, index), 'setup', None)
328
- except SiliconCompilerError:
329
- setup_step = None
330
- if setup_step:
331
- try:
332
- chip.logger.info(f'Setting up node {step}{index} with {tool}/{task}')
333
- setup_ret = setup_step(chip)
334
- except Exception as e:
335
- chip.logger.error(f'Failed to run setup() for {tool}/{task}')
336
- raise e
337
- else:
338
- raise SiliconCompilerError(f'setup() not found for tool {tool}, task {task}', chip=chip)
292
+ setup_ret = task_class.setup()
293
+ except Exception as e:
294
+ chip.logger.error(f'Failed to run setup() for {tool}/{task}')
295
+ raise e
296
+
297
+ task_class.set_runtime(None)
339
298
 
340
299
  # Need to restore step/index, otherwise we will skip setting up other indices.
341
300
  chip.set('option', 'flow', preset_flow)
@@ -344,93 +303,14 @@ def _setup_node(chip, step, index, flow=None):
344
303
 
345
304
  if setup_ret is not None:
346
305
  chip.logger.warning(f'Removing {step}{index} due to {setup_ret}')
347
- chip.set('record', 'status', NodeStatus.SKIPPED, step=step, index=index)
306
+ chip.schema.get("record", field='schema').set('status', NodeStatus.SKIPPED,
307
+ step=step, index=index)
348
308
 
349
309
  return False
350
310
 
351
311
  return True
352
312
 
353
313
 
354
- def _check_version(chip, reported_version, tool, step, index):
355
- # Based on regex for deprecated "legacy specifier" from PyPA packaging
356
- # library. Use this to parse PEP-440ish specifiers with arbitrary
357
- # versions.
358
- _regex_str = r"""
359
- (?P<operator>(==|!=|<=|>=|<|>|~=))
360
- \s*
361
- (?P<version>
362
- [^,;\s)]* # Since this is a "legacy" specifier, and the version
363
- # string can be just about anything, we match everything
364
- # except for whitespace, a semi-colon for marker support,
365
- # a closing paren since versions can be enclosed in
366
- # them, and a comma since it's a version separator.
367
- )
368
- """
369
- _regex = re.compile(r"^\s*" + _regex_str + r"\s*$", re.VERBOSE | re.IGNORECASE)
370
-
371
- normalize_version = getattr(chip._get_tool_module(step, index), 'normalize_version', None)
372
- # Version is good if it matches any of the specifier sets in this list.
373
- spec_sets = chip.get('tool', tool, 'version', step=step, index=index)
374
- if not spec_sets:
375
- return True
376
-
377
- for spec_set in spec_sets:
378
- split_specs = [s.strip() for s in spec_set.split(",") if s.strip()]
379
- specs_list = []
380
- for spec in split_specs:
381
- match = re.match(_regex, spec)
382
- if match is None:
383
- chip.logger.warning(f'Invalid version specifier {spec}. '
384
- f'Defaulting to =={spec}.')
385
- operator = '=='
386
- spec_version = spec
387
- else:
388
- operator = match.group('operator')
389
- spec_version = match.group('version')
390
- specs_list.append((operator, spec_version))
391
-
392
- if normalize_version is None:
393
- normalized_version = reported_version
394
- normalized_specs = ','.join([f'{op}{ver}' for op, ver in specs_list])
395
- else:
396
- try:
397
- normalized_version = normalize_version(reported_version)
398
- except Exception as e:
399
- chip.logger.error(f'Unable to normalize version for {tool}: {reported_version}')
400
- raise e
401
- normalized_spec_list = [f'{op}{normalize_version(ver)}' for op, ver in specs_list]
402
- normalized_specs = ','.join(normalized_spec_list)
403
-
404
- try:
405
- version = packaging.version.Version(normalized_version)
406
- except packaging.version.InvalidVersion:
407
- chip.logger.error(f'Version {reported_version} reported by {tool} does '
408
- 'not match standard.')
409
- if normalize_version is None:
410
- chip.logger.error('Tool driver should implement normalize_version().')
411
- else:
412
- chip.logger.error('normalize_version() returned '
413
- f'invalid version {normalized_version}')
414
-
415
- return False
416
-
417
- try:
418
- spec_set = packaging.specifiers.SpecifierSet(normalized_specs)
419
- except packaging.specifiers.InvalidSpecifier:
420
- chip.logger.error(f'Version specifier set {normalized_specs} '
421
- 'does not match standard.')
422
- return False
423
-
424
- if version in spec_set:
425
- return True
426
-
427
- allowedstr = '; '.join(spec_sets)
428
- chip.logger.error(f"Version check failed for {tool}. Check installation.")
429
- chip.logger.error(f"Found version {reported_version}, "
430
- f"did not satisfy any version specifier set {allowedstr}.")
431
- return False
432
-
433
-
434
314
  ###########################################################################
435
315
  def _runtask(chip, flow, step, index, exec_func, pipe=None, queue=None, replay=False):
436
316
  '''
@@ -457,17 +337,17 @@ def _runtask(chip, flow, step, index, exec_func, pipe=None, queue=None, replay=F
457
337
  chip.set('arg', 'step', step, clobber=True)
458
338
  chip.set('arg', 'index', index, clobber=True)
459
339
 
460
- chip.schema._start_journal()
340
+ chip.schema = JournalingSchema(chip.schema)
341
+ chip.schema.start_journal()
461
342
 
462
343
  # Make record of sc version and machine
463
- __record_version(chip, step, index)
344
+ chip.schema.get("record", field='schema').record_version(step, index)
464
345
  # Record user information if enabled
465
346
  if chip.get('option', 'track', step=step, index=index):
466
- __record_usermachine(chip, step, index)
347
+ chip.schema.get("record", field='schema').record_userinformation(step, index)
467
348
 
468
349
  # Start wall timer
469
- wall_start = time.time()
470
- __record_time(chip, step, index, wall_start, 'start')
350
+ chip.schema.get("record", field='schema').record_time(step, index, RecordTime.START)
471
351
 
472
352
  workdir = _setup_workdir(chip, step, index, replay)
473
353
  cwd = os.getcwd()
@@ -480,12 +360,13 @@ def _runtask(chip, flow, step, index, exec_func, pipe=None, queue=None, replay=F
480
360
 
481
361
  exec_func(chip, step, index, replay)
482
362
  except Exception as e:
483
- print_traceback(chip, e)
363
+ utils.print_traceback(chip.logger, e)
484
364
  _haltstep(chip, chip.get('option', 'flow'), step, index)
485
365
 
486
366
  # return to original directory
487
367
  os.chdir(cwd)
488
- chip.schema._stop_journal()
368
+ chip.schema.stop_journal()
369
+ chip.schema = chip.schema.get_base_schema()
489
370
 
490
371
  if pipe:
491
372
  pipe.send(chip._packages)
@@ -493,7 +374,8 @@ def _runtask(chip, flow, step, index, exec_func, pipe=None, queue=None, replay=F
493
374
 
494
375
  ###########################################################################
495
376
  def _haltstep(chip, flow, step, index, log=True):
496
- chip.set('record', 'status', NodeStatus.ERROR, step=step, index=index)
377
+ chip.schema.get("record", field='schema').set('status', NodeStatus.ERROR,
378
+ step=step, index=index)
497
379
  chip.write_manifest(os.path.join("outputs", f"{chip.get('design')}.pkg.json"))
498
380
 
499
381
  if log:
@@ -520,20 +402,6 @@ def _setupnode(chip, flow, step, index, replay):
520
402
  _haltstep(chip, flow, step, index)
521
403
 
522
404
 
523
- ###########################################################################
524
- def _write_task_manifest(chip, tool, path=None, backup=True):
525
- suffix = chip.get('tool', tool, 'format')
526
- if suffix:
527
- manifest_path = f"sc_manifest.{suffix}"
528
- if path:
529
- manifest_path = os.path.join(path, manifest_path)
530
-
531
- if backup and os.path.exists(manifest_path):
532
- shutil.copyfile(manifest_path, f'{manifest_path}.bak')
533
-
534
- chip.write_manifest(manifest_path, abspath=True)
535
-
536
-
537
405
  ###########################################################################
538
406
  def _setup_workdir(chip, step, index, replay):
539
407
  workdir = chip.getworkdir(step=step, index=index)
@@ -551,27 +419,27 @@ def _select_inputs(chip, step, index, trial=False):
551
419
 
552
420
  flow = chip.get('option', 'flow')
553
421
  tool, _ = get_tool_task(chip, step, index, flow)
554
- sel_inputs = []
555
-
556
- select_inputs = getattr(chip._get_task_module(step, index, flow=flow),
557
- '_select_inputs',
558
- None)
559
- if select_inputs:
560
- log_level = chip.logger.level
561
- if trial:
562
- chip.logger.setLevel(logging.CRITICAL)
563
- sel_inputs = select_inputs(chip, step, index)
564
- if trial:
565
- chip.logger.setLevel(log_level)
566
- else:
567
- sel_inputs = _get_pruned_node_inputs(chip, flow, (step, index))
568
422
 
569
- if (step, index) not in _get_flowgraph_entry_nodes(chip, flow) and not sel_inputs:
423
+ task_class = chip.get("tool", tool, field="schema")
424
+ task_class.set_runtime(chip, step=step, index=index)
425
+
426
+ log_level = chip.logger.level
427
+ if trial:
428
+ chip.logger.setLevel(logging.CRITICAL)
429
+
430
+ sel_inputs = task_class.select_input_nodes()
431
+
432
+ if trial:
433
+ chip.logger.setLevel(log_level)
434
+
435
+ if (step, index) not in chip.schema.get("flowgraph", flow, field="schema").get_entry_nodes() \
436
+ and not sel_inputs:
570
437
  chip.logger.error(f'No inputs selected after running {tool}')
571
438
  _haltstep(chip, flow, step, index)
572
439
 
573
440
  if not trial:
574
- chip.set('record', 'inputnode', sel_inputs, step=step, index=index)
441
+ chip.schema.get("record", field='schema').set('inputnode', sel_inputs,
442
+ step=step, index=index)
575
443
 
576
444
  return sel_inputs
577
445
 
@@ -603,10 +471,18 @@ def _copy_previous_steps_output_data(chip, step, index, replay):
603
471
  '''
604
472
 
605
473
  flow = chip.get('option', 'flow')
606
- if not _get_pruned_node_inputs(chip, flow, (step, index)):
474
+
475
+ flow_schema = chip.schema.get("flowgraph", flow, field="schema")
476
+ runtime = RuntimeFlowgraph(
477
+ flow_schema,
478
+ from_steps=set([step for step, _ in flow_schema.get_entry_nodes()]),
479
+ prune_nodes=chip.get('option', 'prune'))
480
+
481
+ if not runtime.get_node_inputs(step, index, record=chip.schema.get("record", field="schema")):
607
482
  all_inputs = []
608
483
  elif not chip.get('record', 'inputnode', step=step, index=index):
609
- all_inputs = _get_pruned_node_inputs(chip, flow, (step, index))
484
+ all_inputs = runtime.get_node_inputs(step, index,
485
+ record=chip.schema.get("record", field="schema"))
610
486
  else:
611
487
  all_inputs = chip.get('record', 'inputnode', step=step, index=index)
612
488
 
@@ -642,429 +518,9 @@ def _copy_previous_steps_output_data(chip, step, index, replay):
642
518
  os.rename(f'inputs/{outfile.name}', f'inputs/{new_name}')
643
519
 
644
520
 
645
- def __read_std_streams(chip, quiet,
646
- is_stdout_log, stdout_reader, stdout_print,
647
- is_stderr_log, stderr_reader, stderr_print):
648
- '''
649
- Handle directing tool outputs to logger
650
- '''
651
- if not quiet:
652
- if is_stdout_log:
653
- for line in stdout_reader.readlines():
654
- stdout_print(line.rstrip())
655
- if is_stderr_log:
656
- for line in stderr_reader.readlines():
657
- stderr_print(line.rstrip())
658
-
659
-
660
521
  ############################################################################
661
522
  # Chip helper Functions
662
523
  ############################################################################
663
- def _getexe(chip, tool, step, index):
664
- path = chip.get('tool', tool, 'path', step=step, index=index)
665
- exe = chip.get('tool', tool, 'exe')
666
- if exe is None:
667
- return None
668
-
669
- syspath = os.getenv('PATH', os.defpath)
670
- if path:
671
- # Prepend 'path' schema var to system path
672
- syspath = utils._resolve_env_vars(chip, path, step, index) + os.pathsep + syspath
673
-
674
- fullexe = shutil.which(exe, path=syspath)
675
-
676
- return fullexe
677
-
678
-
679
- def _get_run_env_vars(chip, tool, task, step, index, include_path):
680
- envvars = utils.get_env_vars(chip, step, index)
681
- for item in chip.getkeys('tool', tool, 'licenseserver'):
682
- license_file = chip.get('tool', tool, 'licenseserver', item, step=step, index=index)
683
- if license_file:
684
- envvars[item] = ':'.join(license_file)
685
-
686
- if include_path:
687
- path = chip.get('tool', tool, 'path', step=step, index=index)
688
- if path:
689
- envvars['PATH'] = path + os.pathsep + os.environ['PATH']
690
- else:
691
- envvars['PATH'] = os.environ['PATH']
692
-
693
- # Forward additional variables
694
- for var in ('LD_LIBRARY_PATH',):
695
- val = os.getenv(var, None)
696
- if val:
697
- envvars[var] = val
698
-
699
- return envvars
700
-
701
-
702
- #######################################
703
- def _makecmd(chip, tool, task, step, index, script_name='replay.sh', include_path=True):
704
- '''
705
- Constructs a subprocess run command based on eda tool setup.
706
- Creates a replay script in current directory.
707
-
708
- Returns:
709
- runnable command (list)
710
- printable command (str)
711
- command name (str)
712
- command arguments (list)
713
- '''
714
-
715
- fullexe = _getexe(chip, tool, step, index)
716
-
717
- is_posix = __is_posix()
718
-
719
- def parse_options(options):
720
- if not options:
721
- return []
722
- shlex_opts = []
723
- for option in options:
724
- option = option.strip()
725
- if (option.startswith("\"") and option.endswith("\"")) or \
726
- (option.startswith("'") and option.endswith("'")):
727
- # Make sure strings are quoted in double quotes
728
- shlex_opts.append(f'"{option[1:-1]}"')
729
- else:
730
- shlex_opts.extend(shlex.split(option, posix=is_posix))
731
- return shlex_opts
732
-
733
- # Add scripts files
734
- scripts = chip.find_files('tool', tool, 'task', task, 'script', step=step, index=index)
735
-
736
- cmdlist = [fullexe]
737
- cmdlist.extend(parse_options(chip.get('tool', tool, 'task', task, 'option',
738
- step=step, index=index)))
739
- cmdlist.extend(scripts)
740
-
741
- runtime_options = getattr(chip._get_task_module(step, index), 'runtime_options', None)
742
- if not runtime_options:
743
- runtime_options = getattr(chip._get_tool_module(step, index), 'runtime_options', None)
744
- if runtime_options:
745
- try:
746
- chip.schema._start_record_access()
747
- cmdlist.extend(parse_options(runtime_options(chip)))
748
- chip.schema._stop_record_access()
749
- except Exception as e:
750
- chip.logger.error(f'Failed to get runtime options for {tool}/{task}')
751
- raise e
752
-
753
- # Separate variables to be able to display nice name of executable
754
- cmd = os.path.basename(cmdlist[0])
755
- cmd_args = cmdlist[1:]
756
- print_cmd = " ".join([cmd, *cmd_args])
757
- cmdlist = [cmdlist[0]]
758
- for arg in cmd_args:
759
- if arg.startswith("\"") and arg.endswith("\""):
760
- # Remove quoting since subprocess will handle that for us
761
- cmdlist.append(arg[1:-1])
762
- else:
763
- cmdlist.append(arg)
764
-
765
- # create replay file
766
- with open(script_name, 'w') as f:
767
- # Ensure execution runs from the same directory
768
- replay_opts = {}
769
- work_dir = chip.getworkdir(step=step, index=index)
770
- if chip._relative_path:
771
- work_dir = os.path.relpath(work_dir, chip._relative_path)
772
- replay_opts["work_dir"] = work_dir
773
- replay_opts["exports"] = _get_run_env_vars(chip,
774
- tool, task,
775
- step, index,
776
- include_path=include_path)
777
- replay_opts["executable"] = chip.get('tool', tool, 'exe')
778
-
779
- vswitch = chip.get('tool', tool, 'vswitch')
780
- if vswitch:
781
- replay_opts["version_flag"] = " ".join(vswitch)
782
-
783
- format_cmd = [replay_opts["executable"]]
784
- arg_test = re.compile(r'^[-+]')
785
- file_test = re.compile(r'^[/]')
786
- for cmdarg in cmd_args:
787
- add_new_line = len(format_cmd) == 1
788
-
789
- if arg_test.match(cmdarg) or file_test.match(cmdarg):
790
- add_new_line = True
791
- else:
792
- if not arg_test.match(format_cmd[-1]):
793
- add_new_line = True
794
-
795
- if add_new_line:
796
- format_cmd.append(cmdarg)
797
- else:
798
- format_cmd[-1] += f' {cmdarg}'
799
-
800
- replay_opts["cmds"] = format_cmd
801
-
802
- f.write(utils.get_file_template("replay/replay.sh.j2").render(replay_opts))
803
- f.write("\n")
804
-
805
- os.chmod(script_name, 0o755)
806
-
807
- return cmdlist, print_cmd, cmd, cmd_args
808
-
809
-
810
- def __get_stdio(chip, tool, task, flow, step, index):
811
- def get_file(io_type):
812
- suffix = chip.get('tool', tool, 'task', task, io_type, 'suffix',
813
- step=step, index=index)
814
- destination = chip.get('tool', tool, 'task', task, io_type, 'destination',
815
- step=step, index=index)
816
-
817
- io_file = None
818
- if destination == 'log':
819
- io_file = step + "." + suffix
820
- elif destination == 'output':
821
- io_file = os.path.join('outputs', chip.top() + "." + suffix)
822
- elif destination == 'none':
823
- io_file = os.devnull
824
- else:
825
- # This should not happen
826
- chip.logger.error(f'{io_type}/destination has no support for {destination}.')
827
- _haltstep(chip, flow, step, index)
828
-
829
- return io_file
830
-
831
- stdout_file = get_file('stdout')
832
- stderr_file = get_file('stderr')
833
-
834
- return stdout_file, stderr_file
835
-
836
-
837
- def _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, run_func=None):
838
- '''
839
- Run executable (or copy inputs to outputs for builtin functions)
840
- '''
841
-
842
- flow = chip.get('option', 'flow')
843
- tool, task = get_tool_task(chip, step, index, flow)
844
-
845
- quiet = (
846
- chip.get('option', 'quiet', step=step, index=index) and
847
- not chip.get('option', 'breakpoint', step=step, index=index)
848
- )
849
-
850
- stdout_print = chip.logger.info
851
- stderr_print = chip.logger.error
852
- if chip.get('option', 'loglevel', step=step, index=index) == "quiet":
853
- stdout_print = chip.logger.error
854
- stderr_print = chip.logger.error
855
-
856
- # TODO: Currently no memory usage tracking in breakpoints, builtins, or unexpected errors.
857
- max_mem_bytes = 0
858
- cpu_start = time.time()
859
-
860
- stdout_file, stderr_file = __get_stdio(chip, tool, task, flow, step, index)
861
- is_stdout_log = chip.get('tool', tool, 'task', task, 'stdout', 'destination',
862
- step=step, index=index) == 'log'
863
- is_stderr_log = chip.get('tool', tool, 'task', task, 'stderr', 'destination',
864
- step=step, index=index) == 'log' and stderr_file != stdout_file
865
-
866
- chip.logger.info(f'Running in {workdir}')
867
-
868
- retcode = 0
869
- cmdlist = []
870
- cmd_args = []
871
- if run_func:
872
- logfile = None
873
- try:
874
- with open(stdout_file, 'w') as stdout_writer, \
875
- open(stderr_file, 'w') as stderr_writer:
876
- if stderr_file == stdout_file:
877
- stderr_writer.close()
878
- stderr_writer = sys.stdout
879
-
880
- # Handle logger stdout suppression if quiet
881
- stdout_handler_level = chip.logger._console.level
882
- if chip.get('option', 'quiet', step=step, index=index):
883
- chip.logger._console.setLevel(logging.CRITICAL)
884
-
885
- with contextlib.redirect_stderr(stderr_writer), \
886
- contextlib.redirect_stdout(stdout_writer):
887
- retcode = run_func(chip)
888
-
889
- chip.logger._console.setLevel(stdout_handler_level)
890
- except Exception as e:
891
- chip.logger.error(f'Failed in run() for {tool}/{task}: {e}')
892
- retcode = 1 # default to non-zero
893
- print_traceback(chip, e)
894
- chip._error = True
895
- finally:
896
- with sc_open(stdout_file) as stdout_reader, \
897
- sc_open(stderr_file) as stderr_reader:
898
- __read_std_streams(chip,
899
- quiet,
900
- is_stdout_log, stdout_reader, stdout_print,
901
- is_stderr_log, stderr_reader, stderr_print)
902
-
903
- try:
904
- if resource:
905
- # Since memory collection is not possible, collect the current process
906
- # peak memory
907
- max_mem_bytes = max(
908
- max_mem_bytes,
909
- 1024 * resource.getrusage(resource.RUSAGE_SELF).ru_maxrss)
910
- except (OSError, ValueError, PermissionError):
911
- pass
912
- else:
913
- cmdlist, printable_cmd, _, cmd_args = _makecmd(chip, tool, task, step, index)
914
-
915
- ##################
916
- # Make record of tool options
917
- if cmd_args is not None:
918
- chip.set('record', 'toolargs',
919
- ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in cmd_args),
920
- step=step, index=index)
921
-
922
- chip.logger.info('%s', printable_cmd)
923
- timeout = chip.get('option', 'timeout', step=step, index=index)
924
- logfile = step + '.log'
925
- if sys.platform in ('darwin', 'linux') and \
926
- chip.get('option', 'breakpoint', step=step, index=index):
927
- # When we break on a step, the tool often drops into a shell.
928
- # However, our usual subprocess scheme seems to break terminal
929
- # echo for some tools. On POSIX-compatible systems, we can use
930
- # pty to connect the tool to our terminal instead. This code
931
- # doesn't handle quiet/timeout logic, since we don't want either
932
- # of these features for an interactive session. Logic for
933
- # forwarding to file based on
934
- # https://docs.python.org/3/library/pty.html#example.
935
- with open(logfile, 'wb') as log_writer:
936
- def read(fd):
937
- data = os.read(fd, 1024)
938
- log_writer.write(data)
939
- return data
940
- import pty # Note: this import throws exception on Windows
941
- retcode = pty.spawn(cmdlist, read)
942
- else:
943
- with open(stdout_file, 'w') as stdout_writer, \
944
- open(stdout_file, 'r', errors='replace_with_warning') as stdout_reader, \
945
- open(stderr_file, 'w') as stderr_writer, \
946
- open(stderr_file, 'r', errors='replace_with_warning') as stderr_reader:
947
- # if STDOUT and STDERR are to be redirected to the same file,
948
- # use a single writer
949
- if stderr_file == stdout_file:
950
- stderr_writer.close()
951
- stderr_reader.close()
952
- stderr_writer = subprocess.STDOUT
953
-
954
- preexec_fn = None
955
- nice = None
956
- if __is_posix():
957
- nice = chip.get('option', 'nice', step=step, index=index)
958
-
959
- def set_nice():
960
- os.nice(nice)
961
-
962
- if nice:
963
- preexec_fn = set_nice
964
-
965
- cmd_start_time = time.time()
966
- proc = subprocess.Popen(cmdlist,
967
- stdin=subprocess.DEVNULL,
968
- stdout=stdout_writer,
969
- stderr=stderr_writer,
970
- preexec_fn=preexec_fn)
971
- # How long to wait for proc to quit on ctrl-c before force
972
- # terminating.
973
- POLL_INTERVAL = 0.1
974
- MEMORY_WARN_LIMIT = 90
975
- try:
976
- while proc.poll() is None:
977
- # Gather subprocess memory usage.
978
- try:
979
- pproc = psutil.Process(proc.pid)
980
- proc_mem_bytes = pproc.memory_full_info().uss
981
- for child in pproc.children(recursive=True):
982
- proc_mem_bytes += child.memory_full_info().uss
983
- max_mem_bytes = max(max_mem_bytes, proc_mem_bytes)
984
-
985
- memory_usage = psutil.virtual_memory()
986
- if memory_usage.percent > MEMORY_WARN_LIMIT:
987
- chip.logger.warn(
988
- f'Current system memory usage is {memory_usage.percent}%')
989
-
990
- # increase limit warning
991
- MEMORY_WARN_LIMIT = int(memory_usage.percent + 1)
992
- except psutil.Error:
993
- # Process may have already terminated or been killed.
994
- # Retain existing memory usage statistics in this case.
995
- pass
996
- except PermissionError:
997
- # OS is preventing access to this information so it cannot
998
- # be collected
999
- pass
1000
-
1001
- # Loop until process terminates
1002
- __read_std_streams(chip,
1003
- quiet,
1004
- is_stdout_log, stdout_reader, stdout_print,
1005
- is_stderr_log, stderr_reader, stderr_print)
1006
-
1007
- if timeout is not None and time.time() - cmd_start_time > timeout:
1008
- chip.logger.error(f'Step timed out after {timeout} seconds')
1009
- utils.terminate_process(proc.pid)
1010
- raise SiliconCompilerTimeout(f'{step}{index} timeout')
1011
- time.sleep(POLL_INTERVAL)
1012
- except KeyboardInterrupt:
1013
- kill_process(chip, proc, tool, 5 * POLL_INTERVAL, msg="Received ctrl-c. ")
1014
- _haltstep(chip, flow, step, index, log=False)
1015
- except SiliconCompilerTimeout:
1016
- send_messages.send(chip, "timeout", step, index)
1017
- kill_process(chip, proc, tool, 5 * POLL_INTERVAL)
1018
- chip._error = True
1019
-
1020
- # Read the remaining
1021
- __read_std_streams(chip,
1022
- quiet,
1023
- is_stdout_log, stdout_reader, stdout_print,
1024
- is_stderr_log, stderr_reader, stderr_print)
1025
- retcode = proc.returncode
1026
-
1027
- chip.set('record', 'toolexitcode', retcode, step=step, index=index)
1028
- if retcode != 0:
1029
- msg = f'Command failed with code {retcode}.'
1030
- if logfile:
1031
- if quiet:
1032
- # Print last N lines of log when in quiet mode
1033
- with sc_open(logfile) as logfd:
1034
- loglines = logfd.read().splitlines()
1035
- for logline in loglines[-_failed_log_lines:]:
1036
- chip.logger.error(logline)
1037
- # No log file for pure-Python tools.
1038
- msg += f' See log file {os.path.abspath(logfile)}'
1039
- chip.logger.warning(msg)
1040
- chip._error = True
1041
-
1042
- # Capture cpu runtime
1043
- record_metric(chip, step, index, 'exetime', round((time.time() - cpu_start), 2),
1044
- source=None,
1045
- source_unit='s')
1046
-
1047
- # Capture memory usage
1048
- record_metric(chip, step, index, 'memory', max_mem_bytes,
1049
- source=None,
1050
- source_unit='B')
1051
-
1052
-
1053
- def _post_process(chip, step, index):
1054
- flow = chip.get('option', 'flow')
1055
- tool, task = get_tool_task(chip, step, index, flow)
1056
- func = getattr(chip._get_task_module(step, index, flow=flow), 'post_process', None)
1057
- if func:
1058
- try:
1059
- chip.schema._start_record_access()
1060
- func(chip)
1061
- chip.schema._stop_record_access()
1062
- except Exception as e:
1063
- chip.logger.error(f'Failed to run post-process for {tool}/{task}.')
1064
- print_traceback(chip, e)
1065
- chip._error = True
1066
-
1067
-
1068
524
  def _check_logfile(chip, step, index, quiet=False, run_func=None):
1069
525
  '''
1070
526
  Check log file (must be after post-process)
@@ -1107,9 +563,19 @@ def _check_logfile(chip, step, index, quiet=False, run_func=None):
1107
563
  def _executenode(chip, step, index, replay):
1108
564
  workdir = chip.getworkdir(step=step, index=index)
1109
565
  flow = chip.get('option', 'flow')
1110
- tool, _ = get_tool_task(chip, step, index, flow)
566
+ tool, task = get_tool_task(chip, step, index, flow)
1111
567
 
1112
- _pre_process(chip, step, index)
568
+ task_class = chip.get("tool", tool, field="schema")
569
+ task_class.set_runtime(chip)
570
+
571
+ chip.logger.info(f'Running in {workdir}')
572
+
573
+ try:
574
+ task_class.pre_process()
575
+ except Exception as e:
576
+ chip.logger.error(f"Pre-processing failed for '{tool}/{task}'.")
577
+ utils.print_traceback(chip.logger, e)
578
+ raise e
1113
579
 
1114
580
  if chip.get('record', 'status', step=step, index=index) == NodeStatus.SKIPPED:
1115
581
  # copy inputs to outputs and skip execution
@@ -1117,116 +583,68 @@ def _executenode(chip, step, index, replay):
1117
583
 
1118
584
  send_messages.send(chip, "skipped", step, index)
1119
585
  else:
1120
- org_env = _set_env_vars(chip, step, index)
586
+ org_env = os.environ.copy()
587
+ os.environ.update(task_class.get_runtime_environmental_variables())
1121
588
 
1122
- run_func = getattr(chip._get_task_module(step, index, flow=flow), 'run', None)
1123
- toolpath, version = _check_tool_version(chip, step, index, run_func)
589
+ toolpath = task_class.get_exe()
590
+ version = task_class.get_exe_version()
591
+
592
+ if not chip.get('option', 'novercheck', step=step, index=index):
593
+ if not task_class.check_exe_version(version):
594
+ _haltstep(chip, flow, step, index)
1124
595
 
1125
596
  if version:
1126
- chip.set('record', 'toolversion', version, step=step, index=index)
597
+ chip.schema.get("record", field='schema').record_tool(
598
+ step, index, version, RecordTool.VERSION)
1127
599
 
1128
600
  if toolpath:
1129
- chip.set('record', 'toolpath', toolpath, step=step, index=index)
1130
-
1131
- # Write manifest (tool interface) (Don't move this!)
1132
- _write_task_manifest(chip, tool)
601
+ chip.schema.get("record", field='schema').record_tool(
602
+ step, index, toolpath, RecordTool.PATH)
1133
603
 
1134
604
  send_messages.send(chip, "begin", step, index)
1135
605
 
1136
- _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, run_func)
606
+ try:
607
+ task_class.generate_replay_script(
608
+ os.path.join(workdir, "replay.sh"),
609
+ workdir)
610
+ ret_code = task_class.run_task(
611
+ workdir,
612
+ chip.get('option', 'quiet', step=step, index=index),
613
+ chip.get('option', 'loglevel', step=step, index=index),
614
+ chip.get('option', 'breakpoint', step=step, index=index),
615
+ chip.get('option', 'nice', step=step, index=index),
616
+ chip.get('option', 'timeout', step=step, index=index))
617
+ except Exception as e:
618
+ raise e
1137
619
 
1138
620
  os.environ.clear()
1139
621
  os.environ.update(org_env)
1140
622
 
1141
- _post_process(chip, step, index)
1142
-
1143
- _finalizenode(chip, step, index, replay)
1144
-
1145
- send_messages.send(chip, "end", step, index)
1146
-
623
+ if ret_code != 0:
624
+ msg = f'Command failed with code {ret_code}.'
625
+ logfile = f"{step}.log"
626
+ if os.path.exists(logfile):
627
+ if chip.get('option', 'quiet', step=step, index=index):
628
+ # Print last N lines of log when in quiet mode
629
+ with sc_open(logfile) as logfd:
630
+ loglines = logfd.read().splitlines()
631
+ for logline in loglines[-_failed_log_lines:]:
632
+ chip.logger.error(logline)
633
+ # No log file for pure-Python tools.
634
+ msg += f' See log file {os.path.abspath(logfile)}'
635
+ chip.logger.warning(msg)
636
+ chip._error = True
1147
637
 
1148
- def _pre_process(chip, step, index):
1149
- flow = chip.get('option', 'flow')
1150
- tool, task = get_tool_task(chip, step, index, flow)
1151
- func = getattr(chip._get_task_module(step, index, flow=flow), 'pre_process', None)
1152
- if func:
1153
638
  try:
1154
- chip.schema._start_record_access()
1155
- func(chip)
1156
- chip.schema._stop_record_access()
639
+ task_class.post_process()
1157
640
  except Exception as e:
1158
- chip.logger.error(f"Pre-processing failed for '{tool}/{task}'.")
1159
- raise e
1160
- if chip._error:
1161
- chip.logger.error(f"Pre-processing failed for '{tool}/{task}'")
1162
- _haltstep(chip, flow, step, index)
1163
-
1164
-
1165
- def _set_env_vars(chip, step, index):
1166
- org_env = os.environ.copy()
1167
-
1168
- tool, task = get_tool_task(chip, step, index)
1169
-
1170
- chip.schema._start_record_access()
1171
-
1172
- os.environ.update(_get_run_env_vars(chip, tool, task, step, index, include_path=True))
1173
-
1174
- chip.schema._stop_record_access()
1175
-
1176
- return org_env
1177
-
1178
-
1179
- def _check_tool_version(chip, step, index, run_func=None):
1180
- '''
1181
- Check exe version
1182
- '''
641
+ chip.logger.error(f"Post-processing failed for '{tool}/{task}'.")
642
+ utils.print_traceback(chip.logger, e)
643
+ chip._error = True
1183
644
 
1184
- flow = chip.get('option', 'flow')
1185
- tool, task = get_tool_task(chip, step, index, flow)
645
+ _finalizenode(chip, step, index, replay)
1186
646
 
1187
- vercheck = not chip.get('option', 'novercheck', step=step, index=index)
1188
- veropt = chip.get('tool', tool, 'vswitch')
1189
- exe = _getexe(chip, tool, step, index)
1190
- version = None
1191
- if exe is not None:
1192
- exe_path, exe_base = os.path.split(exe)
1193
- if veropt:
1194
- cmdlist = [exe]
1195
- cmdlist.extend(veropt)
1196
- proc = subprocess.run(cmdlist,
1197
- stdin=subprocess.DEVNULL,
1198
- stdout=subprocess.PIPE,
1199
- stderr=subprocess.STDOUT,
1200
- universal_newlines=True)
1201
- if proc.returncode != 0:
1202
- chip.logger.warning(f'Version check on {tool} failed with '
1203
- f'code {proc.returncode}')
1204
-
1205
- parse_version = getattr(chip._get_tool_module(step, index, flow=flow),
1206
- 'parse_version',
1207
- None)
1208
- if parse_version is None:
1209
- chip.logger.error(f'{tool}/{task} does not implement parse_version().')
1210
- _haltstep(chip, flow, step, index)
1211
- try:
1212
- version = parse_version(proc.stdout)
1213
- except Exception as e:
1214
- chip.logger.error(f'{tool} failed to parse version string: {proc.stdout}')
1215
- raise e
1216
-
1217
- chip.logger.info(f"Tool '{exe_base}' found with version '{version}' "
1218
- f"in directory '{exe_path}'")
1219
- if vercheck and not _check_version(chip, version, tool, step, index):
1220
- if proc.returncode != 0:
1221
- chip.logger.error(f"Tool '{exe_base}' responded with: {proc.stdout}")
1222
- _haltstep(chip, flow, step, index)
1223
- else:
1224
- chip.logger.info(f"Tool '{exe_base}' found in directory '{exe_path}'")
1225
- elif run_func is None:
1226
- exe_base = chip.get('tool', tool, 'exe')
1227
- chip.logger.error(f'Executable {exe_base} not found')
1228
- _haltstep(chip, flow, step, index)
1229
- return (exe, version)
647
+ send_messages.send(chip, "end", step, index)
1230
648
 
1231
649
 
1232
650
  def _hash_files(chip, step, index, setup=False):
@@ -1265,7 +683,8 @@ def _hash_files(chip, step, index, setup=False):
1265
683
 
1266
684
 
1267
685
  def _finalizenode(chip, step, index, replay):
1268
- if chip.schema._do_record_access():
686
+ if chip.schema.is_journaling() and any(
687
+ [record["type"] == "get" for record in chip.schema.get_journal()]):
1269
688
  assert_required_accesses(chip, step, index)
1270
689
 
1271
690
  flow = chip.get('option', 'flow')
@@ -1274,12 +693,11 @@ def _finalizenode(chip, step, index, replay):
1274
693
  chip.get('option', 'quiet', step=step, index=index) and not
1275
694
  chip.get('option', 'breakpoint', step=step, index=index)
1276
695
  )
1277
- run_func = getattr(chip._get_task_module(step, index, flow=flow), 'run', None)
1278
696
 
1279
697
  is_skipped = chip.get('record', 'status', step=step, index=index) == NodeStatus.SKIPPED
1280
698
 
1281
699
  if not is_skipped:
1282
- _check_logfile(chip, step, index, quiet, run_func)
700
+ _check_logfile(chip, step, index, quiet, None)
1283
701
 
1284
702
  # Report metrics
1285
703
  for metric in ['errors', 'warnings']:
@@ -1290,30 +708,24 @@ def _finalizenode(chip, step, index, replay):
1290
708
  _hash_files(chip, step, index)
1291
709
 
1292
710
  # Capture wall runtime and cpu cores
1293
- wall_end = time.time()
1294
- __record_time(chip, step, index, wall_end, 'end')
1295
-
1296
- # calculate total time
1297
- total_times = []
1298
- for check_step, check_index in _get_flowgraph_nodes(chip, flow):
1299
- total_time = chip.get('metric', 'totaltime', step=check_step, index=check_index)
1300
- if total_time is not None:
1301
- total_times.append(total_time)
1302
- if total_times:
1303
- total_time = max(total_times)
1304
- else:
1305
- total_time = 0.0
711
+ end_time = chip.schema.get("record", field='schema').record_time(step, index, RecordTime.END)
1306
712
 
1307
- walltime = wall_end - get_record_time(chip, step, index, 'starttime')
713
+ walltime = end_time - chip.schema.get("record", field='schema').get_recorded_time(
714
+ step, index, RecordTime.START)
1308
715
  record_metric(chip, step, index, 'tasktime', walltime,
1309
716
  source=None, source_unit='s')
1310
- record_metric(chip, step, index, 'totaltime', total_time + walltime,
1311
- source=None, source_unit='s')
717
+
718
+ chip.schema.get("metric", field='schema').record_totaltime(
719
+ step, index,
720
+ chip.schema.get("flowgraph", flow, field='schema'),
721
+ chip.schema.get("record", field='schema'))
1312
722
  chip.logger.info(f"Finished task in {round(walltime, 2)}s")
1313
723
 
1314
724
  # Save a successful manifest
1315
725
  if not is_skipped:
1316
- chip.set('record', 'status', NodeStatus.SUCCESS, step=step, index=index)
726
+ chip.schema.get("record", field='schema').set('status', NodeStatus.SUCCESS,
727
+ step=step, index=index)
728
+
1317
729
  chip.write_manifest(os.path.join("outputs", f"{chip.get('design')}.pkg.json"))
1318
730
 
1319
731
  if chip._error and not replay:
@@ -1335,7 +747,7 @@ def _finalizenode(chip, step, index, replay):
1335
747
 
1336
748
  def _make_testcase(chip, step, index):
1337
749
  # Import here to avoid circular import
1338
- from siliconcompiler.issue import generate_testcase
750
+ from siliconcompiler.utils.issue import generate_testcase
1339
751
 
1340
752
  generate_testcase(
1341
753
  chip,
@@ -1378,7 +790,8 @@ def assert_required_accesses(chip, step, index):
1378
790
  if tool == 'builtin':
1379
791
  return
1380
792
 
1381
- gets = chip.schema._get_record_access()
793
+ gets = set([tuple(record["key"]) for record in chip.schema.get_journal()
794
+ if record["type"] == "get"])
1382
795
  logfile = os.path.join(
1383
796
  chip.getworkdir(jobname=jobname, step=step, index=index),
1384
797
  f'{step}.log')
@@ -1418,12 +831,6 @@ def assert_required_accesses(chip, step, index):
1418
831
  for key in chip.getkeys('tool', tool, 'task', task, 'report'):
1419
832
  exempt.append(('tool', tool, 'task', task, 'report', key))
1420
833
 
1421
- # Get exempted keys from task
1422
- func = getattr(chip._get_task_module(step, index, flow=flow), 'exempt_keys', None)
1423
- if func:
1424
- # No need for try / except since this must work properly
1425
- exempt.extend(func(chip))
1426
-
1427
834
  required = set(
1428
835
  [tuple(key.split(',')) for key in chip.get('tool', tool, 'task', task, 'require',
1429
836
  step=step, index=index)])
@@ -1457,20 +864,20 @@ def _reset_flow_nodes(chip, flow, nodes_to_execute):
1457
864
 
1458
865
  def clear_node(step, index):
1459
866
  # Reset metrics and records
867
+ chip.schema.get("metric", field='schema').clear(step, index)
1460
868
  for metric in chip.getkeys('metric'):
1461
869
  _clear_metric(chip, step, index, metric)
1462
- for record in chip.getkeys('record'):
1463
- _clear_record(chip, step, index, record, preserve=[
1464
- 'remoteid',
1465
- 'status',
1466
- 'pythonpackage'])
870
+
871
+ chip.schema.get("record", field='schema').clear(
872
+ step, index, keep=['remoteid', 'status', 'pythonpackage'])
1467
873
 
1468
874
  # Mark all nodes as pending
1469
- for step, index in _get_flowgraph_nodes(chip, flow):
1470
- chip.set('record', 'status', NodeStatus.PENDING, step=step, index=index)
875
+ for step, index in chip.schema.get("flowgraph", flow, field="schema").get_nodes():
876
+ chip.schema.get("record", field='schema').set('status', NodeStatus.PENDING,
877
+ step=step, index=index)
1471
878
 
1472
879
  should_resume = not chip.get('option', 'clean')
1473
- for step, index in _get_flowgraph_nodes(chip, flow):
880
+ for step, index in chip.schema.get("flowgraph", flow, field="schema").get_nodes():
1474
881
  stepdir = chip.getworkdir(step=step, index=index)
1475
882
  cfg = f"{stepdir}/outputs/{chip.get('design')}.pkg.json"
1476
883
 
@@ -1484,12 +891,14 @@ def _reset_flow_nodes(chip, flow, nodes_to_execute):
1484
891
  try:
1485
892
  old_status = Schema(manifest=cfg).get('record', 'status', step=step, index=index)
1486
893
  if old_status:
1487
- chip.set('record', 'status', old_status, step=step, index=index)
894
+ chip.schema.get("record", field='schema').set('status', old_status,
895
+ step=step, index=index)
1488
896
  except Exception:
1489
897
  # unable to load so leave it default
1490
898
  pass
1491
899
  else:
1492
- chip.set('record', 'status', NodeStatus.ERROR, step=step, index=index)
900
+ chip.schema.get("record", field='schema').set('status', NodeStatus.ERROR,
901
+ step=step, index=index)
1493
902
 
1494
903
  for step in chip.getkeys('flowgraph', flow):
1495
904
  all_indices_failed = True
@@ -1505,395 +914,28 @@ def _reset_flow_nodes(chip, flow, nodes_to_execute):
1505
914
  clear_node(step, index)
1506
915
 
1507
916
 
1508
- def _prepare_nodes(chip, nodes_to_run, processes, local_processes, flow):
1509
- '''
1510
- For each node to run, prepare a process and store its dependencies
1511
- '''
1512
-
1513
- # Call this in case this was invoked without __main__
1514
- multiprocessing.freeze_support()
1515
-
1516
- # Log queue for logging messages
1517
- log_queue = multiprocessing.Queue(-1)
1518
-
1519
- init_funcs = set()
1520
- for (step, index) in nodes_to_execute(chip, flow):
1521
- node = (step, index)
1522
-
1523
- if chip.get('record', 'status', step=step, index=index) != NodeStatus.PENDING:
1524
- continue
1525
-
1526
- nodes_to_run[node] = _get_pruned_node_inputs(chip, flow, (step, index))
1527
-
1528
- exec_func = _executenode
1529
-
1530
- if chip.get('option', 'scheduler', 'name', step=step, index=index) == 'slurm':
1531
- # Defer job to compute node
1532
- # If the job is configured to run on a cluster, collect the schema
1533
- # and send it to a compute node for deferred execution.
1534
- init_funcs.add(slurm.init)
1535
- exec_func = slurm._defernode
1536
- elif chip.get('option', 'scheduler', 'name', step=step, index=index) == 'docker':
1537
- # Run job in docker
1538
- init_funcs.add(docker_runner.init)
1539
- exec_func = docker_runner.run
1540
- local_processes.append((step, index))
1541
- else:
1542
- local_processes.append((step, index))
1543
-
1544
- process = {
1545
- "child_pipe": None,
1546
- "parent_pipe": None,
1547
- "proc": None
1548
- }
1549
- process["parent_pipe"], process["child_pipe"] = multiprocessing.Pipe()
1550
- process["proc"] = multiprocessing.Process(
1551
- target=_runtask,
1552
- args=(chip, flow, step, index, exec_func),
1553
- kwargs={"pipe": process["child_pipe"],
1554
- "queue": log_queue})
1555
-
1556
- processes[node] = process
1557
-
1558
- for init_func in init_funcs:
1559
- init_func(chip)
1560
-
1561
- return log_queue
1562
-
1563
-
1564
- def _check_node_dependencies(chip, node, deps, deps_was_successful):
1565
- had_deps = len(deps) > 0
1566
- step, index = node
1567
- tool, _ = get_tool_task(chip, step, index)
1568
-
1569
- # Clear any nodes that have finished from dependency list.
1570
- for in_step, in_index in list(deps):
1571
- in_status = chip.get('record', 'status', step=in_step, index=in_index)
1572
- if NodeStatus.is_done(in_status):
1573
- deps.remove((in_step, in_index))
1574
- if in_status == NodeStatus.SUCCESS:
1575
- deps_was_successful[node] = True
1576
- if NodeStatus.is_error(in_status):
1577
- # Fail if any dependency failed for non-builtin task
1578
- if tool != 'builtin':
1579
- deps.clear()
1580
- chip.set('record', 'status', NodeStatus.ERROR, step=step, index=index)
1581
- return
1582
-
1583
- # Fail if no dependency successfully finished for builtin task
1584
- if had_deps and len(deps) == 0 \
1585
- and tool == 'builtin' and not deps_was_successful.get(node):
1586
- chip.set('record', 'status', NodeStatus.ERROR, step=step, index=index)
1587
-
1588
-
1589
- def _launch_nodes(chip, nodes_to_run, processes, local_processes):
1590
- running_nodes = {}
1591
- max_parallel_run = chip.get('option', 'scheduler', 'maxnodes')
1592
- max_cores = utils.get_cores(chip)
1593
- max_threads = utils.get_cores(chip)
1594
- if not max_parallel_run:
1595
- max_parallel_run = utils.get_cores(chip)
1596
-
1597
- # clip max parallel jobs to 1 <= jobs <= max_cores
1598
- max_parallel_run = max(1, min(max_parallel_run, max_cores))
1599
-
1600
- def allow_start(node):
1601
- if node not in local_processes:
1602
- # using a different scheduler, so allow
1603
- return True, 0
1604
-
1605
- if len(running_nodes) >= max_parallel_run:
1606
- return False, 0
1607
-
1608
- # Record thread count requested
1609
- step, index = node
1610
- tool, task = get_tool_task(chip, step, index)
1611
- requested_threads = chip.get('tool', tool, 'task', task, 'threads',
1612
- step=step, index=index)
1613
- if not requested_threads:
1614
- # not specified, marking it max to be safe
1615
- requested_threads = max_threads
1616
- # clamp to max_parallel to avoid getting locked up
1617
- requested_threads = max(1, min(requested_threads, max_threads))
1618
-
1619
- if requested_threads + sum(running_nodes.values()) > max_cores:
1620
- # delay until there are enough core available
1621
- return False, 0
1622
-
1623
- # allow and record how many threads to associate
1624
- return True, requested_threads
1625
-
1626
- deps_was_successful = {}
1627
-
1628
- if _get_callback('pre_run'):
1629
- _get_callback('pre_run')(chip)
1630
-
1631
- start_times = {None: time.time()}
1632
-
1633
- while len(nodes_to_run) > 0 or len(running_nodes) > 0:
1634
- changed = _process_completed_nodes(chip, processes, running_nodes)
1635
-
1636
- # Check for new nodes that can be launched.
1637
- for node, deps in list(nodes_to_run.items()):
1638
- # TODO: breakpoint logic:
1639
- # if node is breakpoint, then don't launch while len(running_nodes) > 0
1640
-
1641
- _check_node_dependencies(chip, node, deps, deps_was_successful)
1642
-
1643
- if chip.get('record', 'status', step=node[0], index=node[1]) == NodeStatus.ERROR:
1644
- del nodes_to_run[node]
1645
- continue
1646
-
1647
- # If there are no dependencies left, launch this node and
1648
- # remove from nodes_to_run.
1649
- if len(deps) == 0:
1650
- dostart, requested_threads = allow_start(node)
1651
-
1652
- if dostart:
1653
- if _get_callback('pre_node'):
1654
- _get_callback('pre_node')(chip, *node)
1655
-
1656
- chip.set('record', 'status', NodeStatus.RUNNING, step=node[0], index=node[1])
1657
- start_times[node] = time.time()
1658
- changed = True
1659
-
1660
- processes[node]["proc"].start()
1661
- del nodes_to_run[node]
1662
- running_nodes[node] = requested_threads
1663
-
1664
- # Check for situation where we have stuff left to run but don't
1665
- # have any nodes running. This shouldn't happen, but we will get
1666
- # stuck in an infinite loop if it does, so we want to break out
1667
- # with an explicit error.
1668
- if len(nodes_to_run) > 0 and len(running_nodes) == 0:
1669
- raise SiliconCompilerError(
1670
- 'Nodes left to run, but no running nodes. From/to may be invalid.', chip=chip)
1671
-
1672
- if chip._dash and changed:
1673
- # Update dashboard if the manifest changed
1674
- chip._dash.update_manifest(payload={"starttimes": start_times})
1675
-
1676
- if len(running_nodes) == 1:
1677
- # if there is only one node running, just join the thread
1678
- running_node = list(running_nodes.keys())[0]
1679
- processes[running_node]["proc"].join()
1680
- elif len(running_nodes) > 1:
1681
- # if there are more than 1, join the first with a timeout
1682
- running_node = list(running_nodes.keys())[0]
1683
- processes[running_node]["proc"].join(timeout=0.1)
1684
-
1685
-
1686
- def _process_completed_nodes(chip, processes, running_nodes):
1687
- changed = False
1688
- for node in list(running_nodes.keys()):
1689
- if not processes[node]["proc"].is_alive():
1690
- step, index = node
1691
- manifest = os.path.join(chip.getworkdir(step=step, index=index),
1692
- 'outputs',
1693
- f'{chip.design}.pkg.json')
1694
- chip.logger.debug(f'{step}{index} is complete merging: {manifest}')
1695
- if os.path.exists(manifest):
1696
- chip.schema.read_journal(manifest)
1697
-
1698
- if processes[node]["parent_pipe"] and processes[node]["parent_pipe"].poll(1):
1699
- try:
1700
- packages = processes[node]["parent_pipe"].recv()
1701
- if isinstance(packages, dict):
1702
- chip._packages.update(packages)
1703
- except: # noqa E722
1704
- pass
1705
-
1706
- del running_nodes[node]
1707
- if processes[node]["proc"].exitcode > 0:
1708
- status = NodeStatus.ERROR
1709
- else:
1710
- status = chip.get('record', 'status', step=step, index=index)
1711
- if not status or status == NodeStatus.PENDING:
1712
- status = NodeStatus.ERROR
1713
-
1714
- chip.set('record', 'status', status, step=step, index=index)
1715
-
1716
- changed = True
1717
-
1718
- if _get_callback('post_node'):
1719
- _get_callback('post_node')(chip, *node)
1720
-
1721
- return changed
1722
-
1723
-
1724
917
  def _check_nodes_status(chip, flow):
1725
- def success(node):
1726
- return chip.get('record', 'status', step=node[0], index=node[1]) in \
1727
- (NodeStatus.SUCCESS, NodeStatus.SKIPPED)
1728
-
1729
- unreachable_steps = _unreachable_steps_to_execute(chip, flow, cond=success)
1730
- if unreachable_steps:
918
+ flowgraph = chip.schema.get("flowgraph", flow, field="schema")
919
+ runtime = RuntimeFlowgraph(
920
+ flowgraph,
921
+ from_steps=chip.get('option', 'from'),
922
+ to_steps=chip.get('option', 'to'),
923
+ prune_nodes=chip.get('option', 'prune'))
924
+ runtime_no_prune = RuntimeFlowgraph(
925
+ flowgraph,
926
+ from_steps=chip.get('option', 'from'),
927
+ to_steps=chip.get('option', 'to'))
928
+
929
+ all_steps = [step for step, index in runtime_no_prune.get_exit_nodes()
930
+ if (step, index) not in chip.get('option', 'prune')]
931
+ complete_steps = [step for step, _ in runtime.get_completed_nodes(
932
+ record=chip.schema.get("record", field='schema'))]
933
+
934
+ unreached = set(all_steps).difference(complete_steps)
935
+
936
+ if unreached:
1731
937
  raise SiliconCompilerError(
1732
- f'These final steps could not be reached: {list(unreachable_steps)}', chip=chip)
1733
-
1734
-
1735
- #######################################
1736
- def __record_version(chip, step, index):
1737
- chip.set('record', 'scversion', _metadata.version, step=step, index=index)
1738
- chip.set('record', 'pythonversion', platform.python_version(), step=step, index=index)
1739
-
1740
-
1741
- #######################################
1742
- def __record_packages(chip):
1743
- try:
1744
- from pip._internal.operations.freeze import freeze
1745
- except: # noqa E722
1746
- freeze = None
1747
-
1748
- if freeze:
1749
- # clear record
1750
- chip.set('record', 'pythonpackage', [])
1751
-
1752
- for pkg in freeze():
1753
- chip.add('record', 'pythonpackage', pkg)
1754
-
1755
-
1756
- #######################################
1757
- def __record_time(chip, step, index, record_time, timetype):
1758
- formatted_time = datetime.fromtimestamp(record_time).strftime('%Y-%m-%d %H:%M:%S')
1759
-
1760
- if timetype == 'start':
1761
- key = 'starttime'
1762
- elif timetype == 'end':
1763
- key = 'endtime'
1764
- else:
1765
- raise ValueError(f'{timetype} is not a valid time record')
1766
-
1767
- chip.set('record', key, formatted_time, step=step, index=index)
1768
-
1769
-
1770
- def get_record_time(chip, step, index, timetype):
1771
- return datetime.strptime(
1772
- chip.get('record', timetype, step=step, index=index),
1773
- '%Y-%m-%d %H:%M:%S').timestamp()
1774
-
1775
-
1776
- #######################################
1777
- def _get_cloud_region():
1778
- # TODO: add logic to figure out if we're running on a remote cluster and
1779
- # extract the region in a provider-specific way.
1780
- return 'local'
1781
-
1782
-
1783
- #######################################
1784
- def __record_usermachine(chip, step, index):
1785
- machine_info = _get_machine_info()
1786
- chip.set('record', 'platform', machine_info['system'], step=step, index=index)
1787
-
1788
- if machine_info['distro']:
1789
- chip.set('record', 'distro', machine_info['distro'], step=step, index=index)
1790
-
1791
- chip.set('record', 'osversion', machine_info['osversion'], step=step, index=index)
1792
-
1793
- if machine_info['kernelversion']:
1794
- chip.set('record', 'kernelversion', machine_info['kernelversion'], step=step, index=index)
1795
-
1796
- chip.set('record', 'arch', machine_info['arch'], step=step, index=index)
1797
-
1798
- chip.set('record', 'userid', getpass.getuser(), step=step, index=index)
1799
-
1800
- chip.set('record', 'machine', platform.node(), step=step, index=index)
1801
-
1802
- chip.set('record', 'region', _get_cloud_region(), step=step, index=index)
1803
-
1804
- try:
1805
- for interface, addrs in psutil.net_if_addrs().items():
1806
- if interface == 'lo':
1807
- # don't consider loopback device
1808
- continue
1809
-
1810
- if not addrs:
1811
- # skip missing addrs
1812
- continue
1813
-
1814
- use_addr = False
1815
- for addr in addrs:
1816
- if addr.family == socket.AF_INET:
1817
- if not addr.address.startswith('127.'):
1818
- use_addr = True
1819
- break
1820
-
1821
- if use_addr:
1822
- ipaddr = None
1823
- macaddr = None
1824
- for addr in addrs:
1825
- if not ipaddr and addr.family == socket.AF_INET:
1826
- ipaddr = addr.address
1827
- if not ipaddr and addr.family == socket.AF_INET6:
1828
- ipaddr = addr.address
1829
- if not macaddr and addr.family == psutil.AF_LINK:
1830
- macaddr = addr.address
1831
-
1832
- chip.set('record', 'ipaddr', ipaddr, step=step, index=index)
1833
- chip.set('record', 'macaddr', macaddr, step=step, index=index)
1834
- break
1835
- except: # noqa E722
1836
- chip.logger.warning('Could not find default network interface info')
1837
-
1838
-
1839
- #######################################
1840
- def _get_machine_info():
1841
- system = platform.system()
1842
- if system == 'Darwin':
1843
- lower_sys_name = 'macos'
1844
- else:
1845
- lower_sys_name = system.lower()
1846
-
1847
- if system == 'Linux':
1848
- distro_name = distro.id()
1849
- else:
1850
- distro_name = None
1851
-
1852
- if system == 'Darwin':
1853
- osversion, _, _ = platform.mac_ver()
1854
- elif system == 'Linux':
1855
- osversion = distro.version()
1856
- else:
1857
- osversion = platform.release()
1858
-
1859
- if system == 'Linux':
1860
- kernelversion = platform.release()
1861
- elif system == 'Windows':
1862
- kernelversion = platform.version()
1863
- elif system == 'Darwin':
1864
- kernelversion = platform.release()
1865
- else:
1866
- kernelversion = None
1867
-
1868
- arch = platform.machine()
1869
-
1870
- return {'system': lower_sys_name,
1871
- 'distro': distro_name,
1872
- 'osversion': osversion,
1873
- 'kernelversion': kernelversion,
1874
- 'arch': arch}
1875
-
1876
-
1877
- def print_traceback(chip, exception):
1878
- chip.logger.error(f'{exception}')
1879
- trace = StringIO()
1880
- traceback.print_tb(exception.__traceback__, file=trace)
1881
- chip.logger.error("Backtrace:")
1882
- for line in trace.getvalue().splitlines():
1883
- chip.logger.error(line)
1884
-
1885
-
1886
- def kill_process(chip, proc, tool, poll_interval, msg=""):
1887
- TERMINATE_TIMEOUT = 5
1888
- interrupt_time = time.time()
1889
- chip.logger.info(f'{msg}Waiting for {tool} to exit...')
1890
- while proc.poll() is None and \
1891
- (time.time() - interrupt_time) < TERMINATE_TIMEOUT:
1892
- time.sleep(5 * poll_interval)
1893
- if proc.poll() is None:
1894
- chip.logger.warning(f'{tool} did not exit within {TERMINATE_TIMEOUT} '
1895
- 'seconds. Terminating...')
1896
- utils.terminate_process(proc.pid)
938
+ f'These final steps could not be reached: {",".join(sorted(unreached))}', chip=chip)
1897
939
 
1898
940
 
1899
941
  def get_check_node_keys(chip, step, index):
@@ -2146,12 +1188,11 @@ def copy_old_run_dir(chip, org_jobname):
2146
1188
  return
2147
1189
 
2148
1190
  # Copy nodes forward
2149
- org_nodes = set(_nodes_to_execute(
2150
- chip,
2151
- flow,
2152
- _get_flowgraph_entry_nodes(chip, flow),
2153
- from_nodes,
2154
- chip.get('option', 'prune')))
1191
+ runtime = RuntimeFlowgraph(
1192
+ chip.schema.get("flowgraph", flow, field="schema"),
1193
+ to_steps=chip.get('option', 'from'),
1194
+ prune_nodes=chip.get('option', 'prune'))
1195
+ org_nodes = set(runtime.get_nodes())
2155
1196
 
2156
1197
  copy_nodes = org_nodes.difference(from_nodes)
2157
1198
 
@@ -2178,6 +1219,9 @@ def copy_old_run_dir(chip, org_jobname):
2178
1219
 
2179
1220
  # Modify manifests to correct jobname
2180
1221
  for step, index in copy_nodes:
1222
+ tool, _ = get_tool_task(chip, step, index)
1223
+ task_class = chip.get("tool", tool, field="schema")
1224
+
2181
1225
  # rewrite replay files
2182
1226
  replay_file = f'{chip.getworkdir(step=step, index=index)}/replay.sh'
2183
1227
  if os.path.exists(replay_file):
@@ -2185,8 +1229,9 @@ def copy_old_run_dir(chip, org_jobname):
2185
1229
  os.remove(replay_file)
2186
1230
  chip.set('arg', 'step', step)
2187
1231
  chip.set('arg', 'index', index)
2188
- tool, task = get_tool_task(chip, step, index)
2189
- _makecmd(chip, tool, task, step, index, script_name=replay_file)
1232
+ task_class.set_runtime(chip, step=step, index=index)
1233
+ task_class.generate_replay_script(replay_file, chip.getworkdir(step=step, index=index))
1234
+ task_class.set_runtime(None)
2190
1235
  chip.unset('arg', 'step')
2191
1236
  chip.unset('arg', 'index')
2192
1237
 
@@ -2197,8 +1242,7 @@ def copy_old_run_dir(chip, org_jobname):
2197
1242
  # delete file as it might be a hard link
2198
1243
  os.remove(manifest)
2199
1244
  schema.set('option', 'jobname', chip.get('option', 'jobname'))
2200
- with open(manifest, 'w') as f:
2201
- schema.write_json(f)
1245
+ schema.write_manifest(manifest)
2202
1246
 
2203
1247
 
2204
1248
  def clean_node_dir(chip, step, index):
@@ -2224,11 +1268,17 @@ def clean_build_dir(chip):
2224
1268
  return
2225
1269
 
2226
1270
  if chip.get('option', 'from'):
1271
+ runtime = RuntimeFlowgraph(
1272
+ chip.schema.get("flowgraph", chip.get('option', 'flow'), field='schema'),
1273
+ from_steps=chip.get('option', 'from'),
1274
+ to_steps=chip.get('option', 'to'),
1275
+ prune_nodes=chip.get('option', 'prune'))
2227
1276
  # Remove stale outputs that will be rerun
2228
- for step, index in nodes_to_execute(chip):
1277
+ for step, index in runtime.get_nodes():
2229
1278
  clean_node_dir(chip, step, index)
2230
1279
 
2231
- all_nodes = set(_get_flowgraph_nodes(chip, flow=chip.get('option', 'flow')))
1280
+ all_nodes = set(chip.schema.get("flowgraph", chip.get('option', 'flow'),
1281
+ field="schema").get_nodes())
2232
1282
  old_nodes = __collect_nodes_in_workdir(chip)
2233
1283
  node_mismatch = old_nodes.difference(all_nodes)
2234
1284
  if node_mismatch:
@@ -2291,7 +1341,8 @@ def _check_manifest_dynamic(chip, step, index):
2291
1341
  paramtype = chip.get(*keypath, field='type')
2292
1342
  is_perstep = not chip.get(*keypath, field='pernode').is_never()
2293
1343
  if ('file' in paramtype) or ('dir' in paramtype):
2294
- for val, check_step, check_index in chip.schema._getvals(*keypath):
1344
+ for val, check_step, check_index in chip.schema.get(*keypath,
1345
+ field=None).getvalues():
2295
1346
  if is_perstep:
2296
1347
  if check_step is None:
2297
1348
  check_step = Schema.GLOBAL_KEY
@@ -2315,35 +1366,12 @@ def _check_manifest_dynamic(chip, step, index):
2315
1366
 
2316
1367
 
2317
1368
  #######################################
2318
- def _clear_metric(chip, step, index, metric, preserve=None):
1369
+ def _clear_metric(chip, step, index, metric):
2319
1370
  '''
2320
1371
  Helper function to clear metrics records
2321
1372
  '''
2322
1373
 
2323
- # This function is often called in a loop; don't clear
2324
- # metrics which the caller wants to preserve.
2325
- if preserve and metric in preserve:
2326
- return
2327
-
2328
1374
  flow = chip.get('option', 'flow')
2329
1375
  tool, task = get_tool_task(chip, step, index, flow=flow)
2330
1376
 
2331
- chip.unset('metric', metric, step=step, index=index)
2332
1377
  chip.unset('tool', tool, 'task', task, 'report', metric, step=step, index=index)
2333
-
2334
-
2335
- #######################################
2336
- def _clear_record(chip, step, index, record, preserve=None):
2337
- '''
2338
- Helper function to clear record parameters
2339
- '''
2340
-
2341
- # This function is often called in a loop; don't clear
2342
- # records which the caller wants to preserve.
2343
- if preserve and record in preserve:
2344
- return
2345
-
2346
- if chip.get('record', record, field='pernode').is_never():
2347
- chip.unset('record', record)
2348
- else:
2349
- chip.unset('record', record, step=step, index=index)