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.
- siliconcompiler/__init__.py +19 -2
- siliconcompiler/_common.py +5 -0
- siliconcompiler/_metadata.py +1 -1
- siliconcompiler/apps/sc.py +2 -2
- siliconcompiler/apps/sc_install.py +10 -3
- siliconcompiler/apps/sc_issue.py +1 -1
- siliconcompiler/apps/sc_remote.py +10 -5
- siliconcompiler/apps/sc_show.py +2 -2
- siliconcompiler/apps/utils/replay.py +5 -3
- siliconcompiler/asic.py +120 -0
- siliconcompiler/checklist.py +150 -0
- siliconcompiler/core.py +299 -299
- siliconcompiler/flowgraph.py +803 -515
- siliconcompiler/fpga.py +84 -0
- siliconcompiler/metric.py +479 -0
- siliconcompiler/optimizer/vizier.py +2 -3
- siliconcompiler/package/__init__.py +29 -6
- siliconcompiler/pdk.py +415 -0
- siliconcompiler/record.py +453 -0
- siliconcompiler/remote/client.py +15 -5
- siliconcompiler/remote/schema.py +116 -112
- siliconcompiler/remote/server.py +9 -6
- siliconcompiler/report/dashboard/cli/__init__.py +14 -721
- siliconcompiler/report/dashboard/cli/board.py +899 -0
- siliconcompiler/report/dashboard/web/__init__.py +10 -10
- siliconcompiler/report/dashboard/web/components/__init__.py +5 -4
- siliconcompiler/report/dashboard/web/components/flowgraph.py +3 -3
- siliconcompiler/report/dashboard/web/components/graph.py +6 -3
- siliconcompiler/report/dashboard/web/state.py +1 -1
- siliconcompiler/report/dashboard/web/utils/__init__.py +4 -3
- siliconcompiler/report/html_report.py +2 -3
- siliconcompiler/report/report.py +22 -11
- siliconcompiler/report/summary_image.py +1 -1
- siliconcompiler/report/summary_table.py +3 -3
- siliconcompiler/report/utils.py +21 -14
- siliconcompiler/scheduler/__init__.py +234 -1206
- siliconcompiler/scheduler/run_node.py +2 -1
- siliconcompiler/scheduler/send_messages.py +11 -5
- siliconcompiler/scheduler/slurm.py +11 -44
- siliconcompiler/scheduler/taskscheduler.py +320 -0
- siliconcompiler/schema/__init__.py +19 -2
- siliconcompiler/schema/baseschema.py +493 -0
- siliconcompiler/schema/cmdlineschema.py +250 -0
- siliconcompiler/{sphinx_ext → schema/docs}/__init__.py +3 -1
- siliconcompiler/{sphinx_ext → schema/docs}/dynamicgen.py +63 -81
- siliconcompiler/{sphinx_ext → schema/docs}/schemagen.py +73 -85
- siliconcompiler/{sphinx_ext → schema/docs}/utils.py +12 -13
- siliconcompiler/schema/editableschema.py +136 -0
- siliconcompiler/schema/journalingschema.py +238 -0
- siliconcompiler/schema/namedschema.py +41 -0
- siliconcompiler/schema/packageschema.py +101 -0
- siliconcompiler/schema/parameter.py +791 -0
- siliconcompiler/schema/parametertype.py +323 -0
- siliconcompiler/schema/parametervalue.py +736 -0
- siliconcompiler/schema/safeschema.py +37 -0
- siliconcompiler/schema/schema_cfg.py +109 -1789
- siliconcompiler/schema/utils.py +5 -68
- siliconcompiler/schema_obj.py +119 -0
- siliconcompiler/tool.py +1416 -0
- siliconcompiler/tools/_common/__init__.py +6 -10
- siliconcompiler/tools/_common/asic.py +5 -5
- siliconcompiler/tools/_common/sdc/sc_constraints.sdc +1 -1
- siliconcompiler/tools/bluespec/convert.py +9 -8
- siliconcompiler/tools/builtin/_common.py +9 -2
- siliconcompiler/tools/builtin/concatenate.py +7 -3
- siliconcompiler/tools/builtin/minimum.py +7 -2
- siliconcompiler/tools/builtin/mux.py +8 -2
- siliconcompiler/tools/builtin/nop.py +7 -2
- siliconcompiler/tools/builtin/verify.py +11 -5
- siliconcompiler/tools/chisel/convert.py +10 -10
- siliconcompiler/tools/genfasm/bitstream.py +3 -3
- siliconcompiler/tools/ghdl/convert.py +1 -1
- siliconcompiler/tools/icarus/compile.py +4 -4
- siliconcompiler/tools/icepack/bitstream.py +6 -1
- siliconcompiler/tools/klayout/convert_drc_db.py +5 -0
- siliconcompiler/tools/klayout/drc.py +2 -2
- siliconcompiler/tools/klayout/klayout_export.py +0 -1
- siliconcompiler/tools/klayout/klayout_show.py +6 -6
- siliconcompiler/tools/klayout/klayout_utils.py +15 -22
- siliconcompiler/tools/netgen/count_lvs.py +2 -2
- siliconcompiler/tools/netgen/lvs.py +1 -1
- siliconcompiler/tools/nextpnr/apr.py +6 -1
- siliconcompiler/tools/nextpnr/nextpnr.py +4 -4
- siliconcompiler/tools/openroad/_apr.py +15 -2
- siliconcompiler/tools/openroad/rdlroute.py +3 -3
- siliconcompiler/tools/openroad/scripts/apr/postamble.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +5 -5
- siliconcompiler/tools/openroad/scripts/apr/sc_antenna_repair.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_endcap_tapcell_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_fillercell_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_fillmetal_insertion.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -9
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_metrics.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_pin_placement.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +75 -1
- siliconcompiler/tools/openroad/scripts/common/read_input_files.tcl +1 -7
- siliconcompiler/tools/openroad/scripts/common/screenshot.tcl +2 -2
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -3
- siliconcompiler/tools/openroad/scripts/sc_rcx.tcl +1 -1
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/sc_show.tcl +6 -6
- siliconcompiler/tools/opensta/scripts/sc_timing.tcl +10 -0
- siliconcompiler/tools/opensta/timing.py +11 -0
- siliconcompiler/tools/slang/__init__.py +13 -13
- siliconcompiler/tools/slang/elaborate.py +6 -6
- siliconcompiler/tools/slang/lint.py +1 -3
- siliconcompiler/tools/surelog/parse.py +4 -4
- siliconcompiler/tools/sv2v/convert.py +20 -3
- siliconcompiler/tools/verilator/compile.py +2 -2
- siliconcompiler/tools/verilator/verilator.py +3 -3
- siliconcompiler/tools/vpr/_xml_constraint.py +8 -8
- siliconcompiler/tools/vpr/place.py +1 -1
- siliconcompiler/tools/vpr/route.py +4 -4
- siliconcompiler/tools/vpr/screenshot.py +1 -1
- siliconcompiler/tools/vpr/show.py +5 -5
- siliconcompiler/tools/vpr/vpr.py +24 -24
- siliconcompiler/tools/xdm/convert.py +2 -2
- siliconcompiler/tools/xyce/simulate.py +1 -1
- siliconcompiler/tools/yosys/prepareLib.py +2 -2
- siliconcompiler/tools/yosys/sc_synth_asic.tcl +111 -63
- siliconcompiler/tools/yosys/screenshot.py +1 -1
- siliconcompiler/tools/yosys/syn_asic.py +7 -7
- siliconcompiler/toolscripts/_tools.json +12 -10
- siliconcompiler/toolscripts/rhel8/install-chisel.sh +9 -2
- siliconcompiler/toolscripts/rhel8/install-icarus.sh +10 -3
- siliconcompiler/toolscripts/rhel8/install-klayout.sh +8 -1
- siliconcompiler/toolscripts/rhel8/install-magic.sh +9 -2
- siliconcompiler/toolscripts/rhel8/install-montage.sh +1 -1
- siliconcompiler/toolscripts/rhel8/install-netgen.sh +9 -2
- siliconcompiler/toolscripts/rhel8/install-slang.sh +11 -4
- siliconcompiler/toolscripts/rhel8/install-surelog.sh +9 -2
- siliconcompiler/toolscripts/rhel8/install-sv2v.sh +11 -4
- siliconcompiler/toolscripts/rhel8/install-verible.sh +11 -3
- siliconcompiler/toolscripts/rhel8/install-verilator.sh +10 -3
- siliconcompiler/toolscripts/rhel8/install-xyce.sh +15 -10
- siliconcompiler/toolscripts/rhel9/install-chisel.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-ghdl.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-gtkwave.sh +10 -3
- siliconcompiler/toolscripts/rhel9/install-icarus.sh +10 -3
- siliconcompiler/toolscripts/rhel9/install-klayout.sh +8 -1
- siliconcompiler/toolscripts/rhel9/install-magic.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-montage.sh +1 -1
- siliconcompiler/toolscripts/rhel9/install-netgen.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-openroad.sh +16 -3
- siliconcompiler/toolscripts/rhel9/install-opensta.sh +17 -5
- siliconcompiler/toolscripts/rhel9/install-slang.sh +11 -4
- siliconcompiler/toolscripts/rhel9/install-surelog.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-sv2v.sh +11 -4
- siliconcompiler/toolscripts/rhel9/install-verible.sh +11 -3
- siliconcompiler/toolscripts/rhel9/install-verilator.sh +10 -3
- siliconcompiler/toolscripts/rhel9/install-vpr.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-xdm.sh +10 -2
- siliconcompiler/toolscripts/rhel9/install-xyce.sh +15 -10
- siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +9 -2
- siliconcompiler/toolscripts/rhel9/install-yosys-parmys.sh +10 -3
- siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +10 -2
- siliconcompiler/toolscripts/rhel9/install-yosys.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-bambu.sh +10 -2
- siliconcompiler/toolscripts/ubuntu20/install-bluespec.sh +10 -3
- siliconcompiler/toolscripts/ubuntu20/install-chisel.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-ghdl.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-gtkwave.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-icarus.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-icepack.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-klayout.sh +8 -1
- siliconcompiler/toolscripts/ubuntu20/install-magic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-montage.sh +1 -1
- siliconcompiler/toolscripts/ubuntu20/install-netgen.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-nextpnr.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-openroad.sh +16 -3
- siliconcompiler/toolscripts/ubuntu20/install-opensta.sh +16 -5
- siliconcompiler/toolscripts/ubuntu20/install-slang.sh +11 -4
- siliconcompiler/toolscripts/ubuntu20/install-slurm.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-surelog.sh +10 -2
- siliconcompiler/toolscripts/ubuntu20/install-sv2v.sh +11 -4
- siliconcompiler/toolscripts/ubuntu20/install-verible.sh +11 -3
- siliconcompiler/toolscripts/ubuntu20/install-verilator.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-xdm.sh +10 -2
- siliconcompiler/toolscripts/ubuntu20/install-xyce.sh +13 -8
- siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu20/install-yosys.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-bambu.sh +10 -2
- siliconcompiler/toolscripts/ubuntu22/install-bluespec.sh +10 -3
- siliconcompiler/toolscripts/ubuntu22/install-chisel.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-ghdl.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-gtkwave.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-icarus.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-icepack.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-klayout.sh +8 -1
- siliconcompiler/toolscripts/ubuntu22/install-magic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-montage.sh +1 -1
- siliconcompiler/toolscripts/ubuntu22/install-netgen.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-nextpnr.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-openroad.sh +16 -3
- siliconcompiler/toolscripts/ubuntu22/install-opensta.sh +17 -5
- siliconcompiler/toolscripts/ubuntu22/install-slang.sh +11 -4
- siliconcompiler/toolscripts/ubuntu22/install-slurm.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-surelog.sh +10 -2
- siliconcompiler/toolscripts/ubuntu22/install-sv2v.sh +11 -4
- siliconcompiler/toolscripts/ubuntu22/install-verible.sh +11 -3
- siliconcompiler/toolscripts/ubuntu22/install-verilator.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-vpr.sh +9 -4
- siliconcompiler/toolscripts/ubuntu22/install-xdm.sh +10 -2
- siliconcompiler/toolscripts/ubuntu22/install-xyce.sh +13 -8
- siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu22/install-yosys-parmys.sh +10 -3
- siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +10 -2
- siliconcompiler/toolscripts/ubuntu22/install-yosys.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-bambu.sh +12 -4
- siliconcompiler/toolscripts/ubuntu24/install-bluespec.sh +10 -3
- siliconcompiler/toolscripts/ubuntu24/install-chisel.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-ghdl.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-gtkwave.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-icarus.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-icepack.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-klayout.sh +8 -1
- siliconcompiler/toolscripts/ubuntu24/install-magic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-montage.sh +1 -1
- siliconcompiler/toolscripts/ubuntu24/install-netgen.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-nextpnr.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-openroad.sh +16 -3
- siliconcompiler/toolscripts/ubuntu24/install-opensta.sh +17 -5
- siliconcompiler/toolscripts/ubuntu24/install-slang.sh +11 -4
- siliconcompiler/toolscripts/ubuntu24/install-slurm.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-surelog.sh +10 -2
- siliconcompiler/toolscripts/ubuntu24/install-sv2v.sh +11 -4
- siliconcompiler/toolscripts/ubuntu24/install-verible.sh +11 -3
- siliconcompiler/toolscripts/ubuntu24/install-verilator.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-vpr.sh +9 -4
- siliconcompiler/toolscripts/ubuntu24/install-xdm.sh +10 -2
- siliconcompiler/toolscripts/ubuntu24/install-xyce.sh +13 -8
- siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +9 -2
- siliconcompiler/toolscripts/ubuntu24/install-yosys-parmys.sh +10 -3
- siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +10 -2
- siliconcompiler/toolscripts/ubuntu24/install-yosys.sh +9 -2
- siliconcompiler/utils/__init__.py +19 -112
- siliconcompiler/utils/flowgraph.py +244 -0
- siliconcompiler/{issue.py → utils/issue.py} +18 -25
- siliconcompiler/utils/logging.py +3 -4
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/METADATA +9 -8
- siliconcompiler-0.33.1.dist-info/RECORD +488 -0
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/entry_points.txt +8 -8
- siliconcompiler/schema/schema_obj.py +0 -1936
- siliconcompiler/toolscripts/ubuntu20/install-vpr.sh +0 -29
- siliconcompiler/toolscripts/ubuntu20/install-yosys-parmys.sh +0 -61
- siliconcompiler-0.32.3.dist-info/RECORD +0 -470
- /siliconcompiler/{templates → data/templates}/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/email/general.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/email/summary.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/issue/README.txt +0 -0
- /siliconcompiler/{templates → data/templates}/issue/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/issue/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/replay/replay.py.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/replay/replay.sh.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/replay/requirements.txt +0 -0
- /siliconcompiler/{templates → data/templates}/replay/setup.sh +0 -0
- /siliconcompiler/{templates → data/templates}/report/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.css +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap.min.js +0 -0
- /siliconcompiler/{templates → data/templates}/report/bootstrap_LICENSE.md +0 -0
- /siliconcompiler/{templates → data/templates}/report/sc_report.j2 +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/slurm/run.sh +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/__init__.py +0 -0
- /siliconcompiler/{templates → data/templates}/tcl/manifest.tcl.j2 +0 -0
- /siliconcompiler/{units.py → utils/units.py} +0 -0
- {siliconcompiler-0.32.3.dist-info → siliconcompiler-0.33.1.dist-info}/licenses/LICENSE +0 -0
- {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
|
|
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
|
|
27
|
-
from siliconcompiler.
|
|
28
|
-
from siliconcompiler.
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
104
|
-
|
|
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 =
|
|
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
|
-
|
|
185
|
-
chip,
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
194
|
+
nodes = list(runtimeflow.get_nodes())
|
|
210
195
|
all_setup_nodes = nodes + load_nodes + list(extra_setup_nodes.keys())
|
|
211
|
-
for layer_nodes in
|
|
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.
|
|
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.
|
|
229
|
-
|
|
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.
|
|
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
|
|
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
|
|
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.
|
|
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
|
-
|
|
277
|
-
|
|
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
|
-
|
|
328
|
-
except
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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.
|
|
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.
|
|
340
|
+
chip.schema = JournalingSchema(chip.schema)
|
|
341
|
+
chip.schema.start_journal()
|
|
461
342
|
|
|
462
343
|
# Make record of sc version and machine
|
|
463
|
-
|
|
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
|
-
|
|
347
|
+
chip.schema.get("record", field='schema').record_userinformation(step, index)
|
|
467
348
|
|
|
468
349
|
# Start wall timer
|
|
469
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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,
|
|
566
|
+
tool, task = get_tool_task(chip, step, index, flow)
|
|
1111
567
|
|
|
1112
|
-
|
|
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 =
|
|
586
|
+
org_env = os.environ.copy()
|
|
587
|
+
os.environ.update(task_class.get_runtime_environmental_variables())
|
|
1121
588
|
|
|
1122
|
-
|
|
1123
|
-
|
|
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.
|
|
597
|
+
chip.schema.get("record", field='schema').record_tool(
|
|
598
|
+
step, index, version, RecordTool.VERSION)
|
|
1127
599
|
|
|
1128
600
|
if toolpath:
|
|
1129
|
-
chip.
|
|
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
|
-
|
|
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
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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
|
-
|
|
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"
|
|
1159
|
-
|
|
1160
|
-
|
|
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
|
-
|
|
1185
|
-
tool, task = get_tool_task(chip, step, index, flow)
|
|
645
|
+
_finalizenode(chip, step, index, replay)
|
|
1186
646
|
|
|
1187
|
-
|
|
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.
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
1311
|
-
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
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
|
|
1470
|
-
chip.
|
|
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
|
|
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.
|
|
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.
|
|
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
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
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: {
|
|
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
|
-
|
|
2150
|
-
chip,
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
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
|
-
|
|
2189
|
-
|
|
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
|
-
|
|
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
|
|
1277
|
+
for step, index in runtime.get_nodes():
|
|
2229
1278
|
clean_node_dir(chip, step, index)
|
|
2230
1279
|
|
|
2231
|
-
all_nodes = set(
|
|
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.
|
|
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
|
|
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)
|