siliconcompiler 0.30.0__py3-none-any.whl → 0.31.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/_metadata.py +1 -1
- siliconcompiler/apps/sc_install.py +26 -4
- siliconcompiler/apps/sc_remote.py +1 -3
- siliconcompiler/core.py +24 -9
- siliconcompiler/flowgraph.py +11 -23
- siliconcompiler/{package.py → package/__init__.py} +62 -176
- siliconcompiler/package/git.py +81 -0
- siliconcompiler/package/https.py +93 -0
- siliconcompiler/remote/schema.py +9 -8
- siliconcompiler/report/report.py +4 -3
- siliconcompiler/scheduler/__init__.py +127 -113
- siliconcompiler/scheduler/docker_runner.py +4 -4
- siliconcompiler/scheduler/run_node.py +3 -3
- siliconcompiler/scheduler/send_messages.py +1 -1
- siliconcompiler/schema/schema_cfg.py +367 -357
- siliconcompiler/schema/schema_obj.py +39 -29
- siliconcompiler/schema/utils.py +19 -0
- siliconcompiler/sphinx_ext/schemagen.py +3 -1
- siliconcompiler/templates/replay/replay.sh.j2 +92 -0
- siliconcompiler/templates/tcl/manifest.tcl.j2 +1 -1
- siliconcompiler/tools/_common/__init__.py +8 -2
- siliconcompiler/tools/_common/asic.py +1 -1
- siliconcompiler/tools/_common/tcl/sc_pin_constraints.tcl +3 -5
- siliconcompiler/tools/genfasm/genfasm.py +1 -1
- siliconcompiler/tools/klayout/export.py +5 -0
- siliconcompiler/tools/klayout/klayout.py +18 -1
- siliconcompiler/tools/klayout/klayout_export.py +4 -1
- siliconcompiler/tools/klayout/klayout_operations.py +5 -2
- siliconcompiler/tools/klayout/klayout_utils.py +23 -0
- siliconcompiler/tools/klayout/operations.py +5 -0
- siliconcompiler/tools/magic/magic.py +1 -1
- siliconcompiler/tools/openroad/_apr.py +14 -3
- siliconcompiler/tools/openroad/antenna_repair.py +2 -1
- siliconcompiler/tools/openroad/clock_tree_synthesis.py +2 -1
- siliconcompiler/tools/openroad/detailed_placement.py +2 -1
- siliconcompiler/tools/openroad/detailed_route.py +8 -0
- siliconcompiler/tools/openroad/fillercell_insertion.py +2 -1
- siliconcompiler/tools/openroad/global_placement.py +2 -1
- siliconcompiler/tools/openroad/pin_placement.py +2 -1
- siliconcompiler/tools/openroad/rdlroute.py +4 -0
- siliconcompiler/tools/openroad/repair_design.py +2 -1
- siliconcompiler/tools/openroad/repair_timing.py +2 -1
- siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +6 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_clock_tree_synthesis.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +8 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_placement.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +2 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +3 -3
- siliconcompiler/tools/openroad/scripts/apr/sc_macro_placement.tcl +5 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_power_grid.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -0
- siliconcompiler/tools/openroad/scripts/apr/sc_repair_timing.tcl +3 -0
- siliconcompiler/tools/openroad/scripts/common/procs.tcl +29 -12
- siliconcompiler/tools/openroad/scripts/common/reports.tcl +15 -0
- siliconcompiler/tools/openroad/scripts/common/write_images.tcl +28 -0
- siliconcompiler/tools/openroad/scripts/sc_rdlroute.tcl +3 -13
- siliconcompiler/tools/vpr/vpr.py +86 -6
- siliconcompiler/tools/yosys/__init__.py +7 -0
- siliconcompiler/tools/yosys/sc_syn.tcl +33 -24
- siliconcompiler/tools/yosys/syn_asic.py +27 -0
- siliconcompiler/tools/yosys/syn_asic.tcl +27 -0
- siliconcompiler/toolscripts/_tools.json +15 -3
- siliconcompiler/toolscripts/rhel8/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/rhel8/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/rhel9/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu20/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu22/install-yosys-slang.sh +22 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-moosic.sh +17 -0
- siliconcompiler/toolscripts/ubuntu24/install-yosys-slang.sh +22 -0
- siliconcompiler/utils/__init__.py +33 -5
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/METADATA +21 -23
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/RECORD +79 -66
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/WHEEL +1 -1
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/entry_points.txt +4 -0
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/LICENSE +0 -0
- {siliconcompiler-0.30.0.dist-info → siliconcompiler-0.31.1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import requests
|
|
2
|
+
import shutil
|
|
3
|
+
import tarfile
|
|
4
|
+
import zipfile
|
|
5
|
+
|
|
6
|
+
import os.path
|
|
7
|
+
|
|
8
|
+
from fasteners import InterProcessLock
|
|
9
|
+
from io import BytesIO
|
|
10
|
+
from urllib.parse import urlparse
|
|
11
|
+
|
|
12
|
+
from siliconcompiler import SiliconCompilerError
|
|
13
|
+
from siliconcompiler.package import get_download_cache_path
|
|
14
|
+
from siliconcompiler.package import aquire_data_lock, release_data_lock
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def get_resolver(url):
|
|
18
|
+
if url.scheme in ("http", "https"):
|
|
19
|
+
return http_resolver
|
|
20
|
+
|
|
21
|
+
return None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def http_resolver(chip, package, path, ref, url):
|
|
25
|
+
data_path, data_path_lock = get_download_cache_path(chip, package, ref)
|
|
26
|
+
|
|
27
|
+
if os.path.exists(data_path):
|
|
28
|
+
return data_path, False
|
|
29
|
+
|
|
30
|
+
# Acquire lock
|
|
31
|
+
data_lock = InterProcessLock(data_path_lock)
|
|
32
|
+
aquire_data_lock(data_path, data_lock)
|
|
33
|
+
|
|
34
|
+
extract_from_url(chip, package, path, ref, url, data_path)
|
|
35
|
+
|
|
36
|
+
release_data_lock(data_lock)
|
|
37
|
+
|
|
38
|
+
return data_path, True
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def extract_from_url(chip, package, path, ref, url, data_path):
|
|
42
|
+
data_url = path
|
|
43
|
+
headers = {}
|
|
44
|
+
if os.environ.get('GIT_TOKEN') or url.username:
|
|
45
|
+
headers['Authorization'] = f'token {os.environ.get("GIT_TOKEN") or url.username}'
|
|
46
|
+
if "github" in data_url:
|
|
47
|
+
headers['Accept'] = 'application/octet-stream'
|
|
48
|
+
data_url = path
|
|
49
|
+
if data_url.endswith('/'):
|
|
50
|
+
data_url = f"{data_url}{ref}.tar.gz"
|
|
51
|
+
chip.logger.info(f'Downloading {package} data from {data_url}')
|
|
52
|
+
response = requests.get(data_url, stream=True, headers=headers)
|
|
53
|
+
if not response.ok:
|
|
54
|
+
raise SiliconCompilerError(f'Failed to download {package} data source.', chip=chip)
|
|
55
|
+
|
|
56
|
+
fileobj = BytesIO(response.content)
|
|
57
|
+
try:
|
|
58
|
+
with tarfile.open(fileobj=fileobj, mode='r|gz') as tar_ref:
|
|
59
|
+
tar_ref.extractall(path=data_path)
|
|
60
|
+
except tarfile.ReadError:
|
|
61
|
+
fileobj.seek(0)
|
|
62
|
+
# Try as zip
|
|
63
|
+
with zipfile.ZipFile(fileobj) as zip_ref:
|
|
64
|
+
zip_ref.extractall(path=data_path)
|
|
65
|
+
|
|
66
|
+
if 'github' in url.netloc and len(os.listdir(data_path)) == 1:
|
|
67
|
+
# Github inserts one folder at the highest level of the tar file
|
|
68
|
+
# this compensates for this behavior
|
|
69
|
+
gh_url = urlparse(data_url)
|
|
70
|
+
|
|
71
|
+
repo = gh_url.path.split('/')[2]
|
|
72
|
+
|
|
73
|
+
gh_ref = gh_url.path.split('/')[-1]
|
|
74
|
+
if repo.endswith('.git'):
|
|
75
|
+
gh_ref = ref
|
|
76
|
+
elif gh_ref.endswith('.tar.gz'):
|
|
77
|
+
gh_ref = gh_ref[0:-7]
|
|
78
|
+
elif gh_ref.endswith('.tgz'):
|
|
79
|
+
gh_ref = gh_ref[0:-4]
|
|
80
|
+
else:
|
|
81
|
+
gh_ref = gh_ref.split('.')[0]
|
|
82
|
+
|
|
83
|
+
if gh_ref.startswith('v'):
|
|
84
|
+
gh_ref = gh_ref[1:]
|
|
85
|
+
|
|
86
|
+
github_folder = f"{repo}-{gh_ref}"
|
|
87
|
+
|
|
88
|
+
if github_folder in os.listdir(data_path):
|
|
89
|
+
# This moves all files one level up
|
|
90
|
+
git_path = os.path.join(data_path, github_folder)
|
|
91
|
+
for data_file in os.listdir(git_path):
|
|
92
|
+
shutil.move(os.path.join(git_path, data_file), data_path)
|
|
93
|
+
os.removedirs(git_path)
|
siliconcompiler/remote/schema.py
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from siliconcompiler.schema.schema_cfg import scparam
|
|
2
2
|
from siliconcompiler.schema import Schema
|
|
3
|
+
from siliconcompiler.schema.utils import PerNode, Scope
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
SCHEMA_VERSION = '0.0.2'
|
|
@@ -11,7 +12,7 @@ def schema_cfg():
|
|
|
11
12
|
|
|
12
13
|
scparam(cfg, ['schemaversion'],
|
|
13
14
|
sctype='str',
|
|
14
|
-
scope=
|
|
15
|
+
scope=Scope.GLOBAL,
|
|
15
16
|
defvalue=SCHEMA_VERSION,
|
|
16
17
|
require='all',
|
|
17
18
|
shorthelp="Schema version number",
|
|
@@ -22,7 +23,7 @@ def schema_cfg():
|
|
|
22
23
|
|
|
23
24
|
scparam(cfg, ['option', 'port'],
|
|
24
25
|
sctype='int',
|
|
25
|
-
scope=
|
|
26
|
+
scope=Scope.GLOBAL,
|
|
26
27
|
defvalue=8080,
|
|
27
28
|
require='all',
|
|
28
29
|
shorthelp="Port number to run the server on.",
|
|
@@ -34,7 +35,7 @@ def schema_cfg():
|
|
|
34
35
|
scparam(cfg, ['option', 'cluster'],
|
|
35
36
|
sctype='enum',
|
|
36
37
|
enum=['local', 'slurm'],
|
|
37
|
-
scope=
|
|
38
|
+
scope=Scope.GLOBAL,
|
|
38
39
|
defvalue='local',
|
|
39
40
|
require='all',
|
|
40
41
|
shorthelp="Type of compute cluster to use.",
|
|
@@ -45,7 +46,7 @@ def schema_cfg():
|
|
|
45
46
|
|
|
46
47
|
scparam(cfg, ['option', 'nfsmount'],
|
|
47
48
|
sctype='dir',
|
|
48
|
-
scope=
|
|
49
|
+
scope=Scope.GLOBAL,
|
|
49
50
|
defvalue='/nfs/sc_compute',
|
|
50
51
|
require='all',
|
|
51
52
|
shorthelp="Directory of mounted shared NFS storage.",
|
|
@@ -56,7 +57,7 @@ def schema_cfg():
|
|
|
56
57
|
|
|
57
58
|
scparam(cfg, ['option', 'auth'],
|
|
58
59
|
sctype='bool',
|
|
59
|
-
scope=
|
|
60
|
+
scope=Scope.GLOBAL,
|
|
60
61
|
defvalue=False,
|
|
61
62
|
require='all',
|
|
62
63
|
shorthelp="Flag determining whether to enable authenticated and encrypted jobs.",
|
|
@@ -67,7 +68,7 @@ def schema_cfg():
|
|
|
67
68
|
|
|
68
69
|
scparam(cfg, ['option', 'cfg'],
|
|
69
70
|
sctype='[file]',
|
|
70
|
-
scope=
|
|
71
|
+
scope=Scope.JOB,
|
|
71
72
|
shorthelp="Configuration manifest",
|
|
72
73
|
switch="-cfg <file>",
|
|
73
74
|
example=["cli: -cfg mypdk.json",
|
|
@@ -82,8 +83,8 @@ def schema_cfg():
|
|
|
82
83
|
scparam(cfg, ['option', 'loglevel'],
|
|
83
84
|
sctype='enum',
|
|
84
85
|
enum=["info", "warning", "error", "critical", "debug"],
|
|
85
|
-
pernode=
|
|
86
|
-
scope=
|
|
86
|
+
pernode=PerNode.OPTIONAL,
|
|
87
|
+
scope=Scope.JOB,
|
|
87
88
|
defvalue='info',
|
|
88
89
|
shorthelp="Logging level",
|
|
89
90
|
switch="-loglevel <str>",
|
siliconcompiler/report/report.py
CHANGED
|
@@ -2,6 +2,7 @@ import fnmatch
|
|
|
2
2
|
import pandas
|
|
3
3
|
import os
|
|
4
4
|
from siliconcompiler import Schema
|
|
5
|
+
from siliconcompiler.schema.utils import PerNode
|
|
5
6
|
from siliconcompiler.report import utils
|
|
6
7
|
from siliconcompiler.flowgraph import nodes_to_execute
|
|
7
8
|
from siliconcompiler.tools._common import get_tool_task
|
|
@@ -53,7 +54,7 @@ def get_flowgraph_nodes(chip, step, index):
|
|
|
53
54
|
if task is not None:
|
|
54
55
|
nodes['task'] = task
|
|
55
56
|
for key in chip.getkeys('record'):
|
|
56
|
-
if chip.get('record', key, field='pernode')
|
|
57
|
+
if chip.get('record', key, field='pernode').is_never():
|
|
57
58
|
value = chip.get('record', key)
|
|
58
59
|
else:
|
|
59
60
|
value = chip.get('record', key, step=step, index=index)
|
|
@@ -106,7 +107,7 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
|
|
|
106
107
|
'''
|
|
107
108
|
|
|
108
109
|
def build_leaf(manifest_subsect):
|
|
109
|
-
if manifest_subsect['pernode'] ==
|
|
110
|
+
if PerNode(manifest_subsect['pernode']) == PerNode.NEVER:
|
|
110
111
|
if Schema.GLOBAL_KEY in manifest_subsect['node'] and \
|
|
111
112
|
Schema.GLOBAL_KEY in manifest_subsect['node'][Schema.GLOBAL_KEY]:
|
|
112
113
|
value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
|
|
@@ -132,7 +133,7 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
|
|
|
132
133
|
return node_values
|
|
133
134
|
|
|
134
135
|
if Schema._is_leaf(manifest_subsect):
|
|
135
|
-
if manifest_subsect['pernode'] ==
|
|
136
|
+
if PerNode(manifest_subsect['pernode']) == PerNode.NEVER:
|
|
136
137
|
if Schema.GLOBAL_KEY in manifest_subsect['node']:
|
|
137
138
|
value = manifest_subsect['node'][Schema.GLOBAL_KEY][Schema.GLOBAL_KEY]['value']
|
|
138
139
|
else:
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import contextlib
|
|
2
|
-
import copy
|
|
3
2
|
import distro
|
|
4
3
|
import getpass
|
|
5
4
|
import multiprocessing
|
|
@@ -27,7 +26,7 @@ from siliconcompiler.scheduler import slurm
|
|
|
27
26
|
from siliconcompiler.scheduler import docker_runner
|
|
28
27
|
from siliconcompiler import NodeStatus, SiliconCompilerError
|
|
29
28
|
from siliconcompiler.flowgraph import _get_flowgraph_nodes, _get_flowgraph_execution_order, \
|
|
30
|
-
_get_pruned_node_inputs,
|
|
29
|
+
_get_pruned_node_inputs, _get_flowgraph_entry_nodes, \
|
|
31
30
|
_unreachable_steps_to_execute, _nodes_to_execute, \
|
|
32
31
|
get_nodes_from, nodes_to_execute, _check_flowgraph
|
|
33
32
|
from siliconcompiler.tools._common import input_file_node_name
|
|
@@ -99,13 +98,6 @@ def run(chip):
|
|
|
99
98
|
_reset_flow_nodes(chip, flow, nodes_to_execute(chip, flow))
|
|
100
99
|
__record_packages(chip)
|
|
101
100
|
|
|
102
|
-
# Save current environment
|
|
103
|
-
environment = copy.deepcopy(os.environ)
|
|
104
|
-
# Set env variables
|
|
105
|
-
for envvar in chip.getkeys('option', 'env'):
|
|
106
|
-
val = chip.get('option', 'env', envvar)
|
|
107
|
-
os.environ[envvar] = val
|
|
108
|
-
|
|
109
101
|
if chip.get('option', 'remote'):
|
|
110
102
|
client = Client(chip)
|
|
111
103
|
client.run()
|
|
@@ -113,23 +105,18 @@ def run(chip):
|
|
|
113
105
|
_local_process(chip, flow)
|
|
114
106
|
|
|
115
107
|
# Merge cfgs from last executed tasks, and write out a final manifest.
|
|
116
|
-
_finalize_run(chip
|
|
108
|
+
_finalize_run(chip)
|
|
117
109
|
|
|
118
110
|
|
|
119
111
|
###########################################################################
|
|
120
|
-
def _finalize_run(chip
|
|
112
|
+
def _finalize_run(chip):
|
|
121
113
|
'''
|
|
122
114
|
Helper function to finalize a job run after it completes:
|
|
123
|
-
* Restore any environment variable changes made during the run.
|
|
124
115
|
* Clear any -arg_step/-arg_index values in case only one node was run.
|
|
125
116
|
* Store this run in the Schema's 'history' field.
|
|
126
117
|
* Write out a final JSON manifest containing the full results and history.
|
|
127
118
|
'''
|
|
128
119
|
|
|
129
|
-
# Restore environment
|
|
130
|
-
os.environ.clear()
|
|
131
|
-
os.environ.update(environment)
|
|
132
|
-
|
|
133
120
|
# Clear scratchpad args since these are checked on run() entry
|
|
134
121
|
chip.set('arg', 'step', None, clobber=True)
|
|
135
122
|
chip.set('arg', 'index', None, clobber=True)
|
|
@@ -502,10 +489,13 @@ def _haltstep(chip, flow, step, index, log=True):
|
|
|
502
489
|
def _setupnode(chip, flow, step, index, replay):
|
|
503
490
|
_hash_files(chip, step, index, setup=True)
|
|
504
491
|
|
|
492
|
+
# Select the inputs to this node
|
|
493
|
+
_select_inputs(chip, step, index)
|
|
494
|
+
|
|
505
495
|
# Write manifest prior to step running into inputs
|
|
506
496
|
chip.write_manifest(f'inputs/{chip.get("design")}.pkg.json')
|
|
507
497
|
|
|
508
|
-
|
|
498
|
+
# Forward data
|
|
509
499
|
_copy_previous_steps_output_data(chip, step, index, replay)
|
|
510
500
|
|
|
511
501
|
# Check manifest
|
|
@@ -541,7 +531,7 @@ def _setup_workdir(chip, step, index, replay):
|
|
|
541
531
|
return workdir
|
|
542
532
|
|
|
543
533
|
|
|
544
|
-
def _select_inputs(chip, step, index):
|
|
534
|
+
def _select_inputs(chip, step, index, trial=False):
|
|
545
535
|
|
|
546
536
|
flow = chip.get('option', 'flow')
|
|
547
537
|
tool, _ = get_tool_task(chip, step, index, flow)
|
|
@@ -551,15 +541,24 @@ def _select_inputs(chip, step, index):
|
|
|
551
541
|
'_select_inputs',
|
|
552
542
|
None)
|
|
553
543
|
if select_inputs:
|
|
544
|
+
log_handlers = None
|
|
545
|
+
if trial:
|
|
546
|
+
log_handlers = chip.logger.handlers.copy()
|
|
547
|
+
chip.logger.handlers.clear()
|
|
554
548
|
sel_inputs = select_inputs(chip, step, index)
|
|
549
|
+
if log_handlers:
|
|
550
|
+
chip.logger.handlers = log_handlers
|
|
555
551
|
else:
|
|
556
|
-
sel_inputs =
|
|
552
|
+
sel_inputs = _get_pruned_node_inputs(chip, flow, (step, index))
|
|
557
553
|
|
|
558
554
|
if (step, index) not in _get_flowgraph_entry_nodes(chip, flow) and not sel_inputs:
|
|
559
555
|
chip.logger.error(f'No inputs selected after running {tool}')
|
|
560
556
|
_haltstep(chip, flow, step, index)
|
|
561
557
|
|
|
562
|
-
|
|
558
|
+
if not trial:
|
|
559
|
+
chip.set('record', 'inputnode', sel_inputs, step=step, index=index)
|
|
560
|
+
|
|
561
|
+
return sel_inputs
|
|
563
562
|
|
|
564
563
|
|
|
565
564
|
def copy_output_file(chip, outfile, folder='inputs'):
|
|
@@ -608,8 +607,14 @@ def _copy_previous_steps_output_data(chip, step, index, replay):
|
|
|
608
607
|
# configuration into inputs/{design}.pkg.json earlier in _runstep.
|
|
609
608
|
if not replay:
|
|
610
609
|
in_workdir = chip.getworkdir(step=in_step, index=in_index)
|
|
610
|
+
output_dir = os.path.join(in_workdir, "outputs")
|
|
611
611
|
|
|
612
|
-
|
|
612
|
+
if not os.path.isdir(output_dir):
|
|
613
|
+
chip.logger.error(
|
|
614
|
+
f'Unable to locate outputs directory for {in_step}{in_index}: {output_dir}')
|
|
615
|
+
_haltstep(chip, flow, step, index)
|
|
616
|
+
|
|
617
|
+
for outfile in os.scandir(output_dir):
|
|
613
618
|
new_name = input_file_node_name(outfile.name, in_step, in_index)
|
|
614
619
|
if strict:
|
|
615
620
|
if outfile.name not in in_files and new_name not in in_files:
|
|
@@ -649,13 +654,36 @@ def _getexe(chip, tool, step, index):
|
|
|
649
654
|
syspath = os.getenv('PATH', os.defpath)
|
|
650
655
|
if path:
|
|
651
656
|
# Prepend 'path' schema var to system path
|
|
652
|
-
syspath = utils._resolve_env_vars(chip, path) + os.pathsep + syspath
|
|
657
|
+
syspath = utils._resolve_env_vars(chip, path, step, index) + os.pathsep + syspath
|
|
653
658
|
|
|
654
659
|
fullexe = shutil.which(exe, path=syspath)
|
|
655
660
|
|
|
656
661
|
return fullexe
|
|
657
662
|
|
|
658
663
|
|
|
664
|
+
def _get_run_env_vars(chip, tool, task, step, index, include_path):
|
|
665
|
+
envvars = utils.get_env_vars(chip, step, index)
|
|
666
|
+
for item in chip.getkeys('tool', tool, 'licenseserver'):
|
|
667
|
+
license_file = chip.get('tool', tool, 'licenseserver', item, step=step, index=index)
|
|
668
|
+
if license_file:
|
|
669
|
+
envvars[item] = ':'.join(license_file)
|
|
670
|
+
|
|
671
|
+
if include_path:
|
|
672
|
+
path = chip.get('tool', tool, 'path', step=step, index=index)
|
|
673
|
+
if path:
|
|
674
|
+
envvars['PATH'] = path + os.pathsep + os.environ['PATH']
|
|
675
|
+
else:
|
|
676
|
+
envvars['PATH'] = os.environ['PATH']
|
|
677
|
+
|
|
678
|
+
# Forward additional variables
|
|
679
|
+
for var in ('LD_LIBRARY_PATH',):
|
|
680
|
+
val = os.getenv(var, None)
|
|
681
|
+
if val:
|
|
682
|
+
envvars[var] = val
|
|
683
|
+
|
|
684
|
+
return envvars
|
|
685
|
+
|
|
686
|
+
|
|
659
687
|
#######################################
|
|
660
688
|
def _makecmd(chip, tool, task, step, index, script_name='replay.sh', include_path=True):
|
|
661
689
|
'''
|
|
@@ -707,32 +735,6 @@ def _makecmd(chip, tool, task, step, index, script_name='replay.sh', include_pat
|
|
|
707
735
|
chip.logger.error(f'Failed to get runtime options for {tool}/{task}')
|
|
708
736
|
raise e
|
|
709
737
|
|
|
710
|
-
envvars = {}
|
|
711
|
-
for key in chip.getkeys('option', 'env'):
|
|
712
|
-
envvars[key] = chip.get('option', 'env', key)
|
|
713
|
-
for item in chip.getkeys('tool', tool, 'licenseserver'):
|
|
714
|
-
license_file = chip.get('tool', tool, 'licenseserver', item, step=step, index=index)
|
|
715
|
-
if license_file:
|
|
716
|
-
envvars[item] = ':'.join(license_file)
|
|
717
|
-
|
|
718
|
-
if include_path:
|
|
719
|
-
path = chip.get('tool', tool, 'path', step=step, index=index)
|
|
720
|
-
if path:
|
|
721
|
-
envvars['PATH'] = path + os.pathsep + os.environ['PATH']
|
|
722
|
-
else:
|
|
723
|
-
envvars['PATH'] = os.environ['PATH']
|
|
724
|
-
|
|
725
|
-
# Forward additional variables
|
|
726
|
-
for var in ('LD_LIBRARY_PATH',):
|
|
727
|
-
val = os.getenv(var, None)
|
|
728
|
-
if val:
|
|
729
|
-
envvars[var] = val
|
|
730
|
-
|
|
731
|
-
for key in chip.getkeys('tool', tool, 'task', task, 'env'):
|
|
732
|
-
val = chip.get('tool', tool, 'task', task, 'env', key, step=step, index=index)
|
|
733
|
-
if val:
|
|
734
|
-
envvars[key] = val
|
|
735
|
-
|
|
736
738
|
# Separate variables to be able to display nice name of executable
|
|
737
739
|
cmd = os.path.basename(cmdlist[0])
|
|
738
740
|
cmd_args = cmdlist[1:]
|
|
@@ -747,19 +749,23 @@ def _makecmd(chip, tool, task, step, index, script_name='replay.sh', include_pat
|
|
|
747
749
|
|
|
748
750
|
# create replay file
|
|
749
751
|
with open(script_name, 'w') as f:
|
|
750
|
-
print('#!/usr/bin/env bash', file=f)
|
|
751
|
-
|
|
752
|
-
envvar_cmd = 'export'
|
|
753
|
-
for key, val in envvars.items():
|
|
754
|
-
print(f'{envvar_cmd} {key}="{val}"', file=f)
|
|
755
|
-
|
|
756
752
|
# Ensure execution runs from the same directory
|
|
753
|
+
replay_opts = {}
|
|
757
754
|
work_dir = chip.getworkdir(step=step, index=index)
|
|
758
755
|
if chip._relative_path:
|
|
759
756
|
work_dir = os.path.relpath(work_dir, chip._relative_path)
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
757
|
+
replay_opts["work_dir"] = work_dir
|
|
758
|
+
replay_opts["exports"] = _get_run_env_vars(chip,
|
|
759
|
+
tool, task,
|
|
760
|
+
step, index,
|
|
761
|
+
include_path=include_path)
|
|
762
|
+
replay_opts["executable"] = chip.get('tool', tool, 'exe')
|
|
763
|
+
|
|
764
|
+
vswitch = chip.get('tool', tool, 'vswitch')
|
|
765
|
+
if vswitch:
|
|
766
|
+
replay_opts["version_flag"] = " ".join(vswitch)
|
|
767
|
+
|
|
768
|
+
format_cmd = [replay_opts["executable"]]
|
|
763
769
|
arg_test = re.compile(r'^[-+]')
|
|
764
770
|
file_test = re.compile(r'^[/]')
|
|
765
771
|
for cmdarg in cmd_args:
|
|
@@ -775,7 +781,11 @@ def _makecmd(chip, tool, task, step, index, script_name='replay.sh', include_pat
|
|
|
775
781
|
format_cmd.append(cmdarg)
|
|
776
782
|
else:
|
|
777
783
|
format_cmd[-1] += f' {cmdarg}'
|
|
778
|
-
|
|
784
|
+
|
|
785
|
+
replay_opts["cmds"] = format_cmd
|
|
786
|
+
|
|
787
|
+
f.write(utils.get_file_template("replay/replay.sh.j2").render(replay_opts))
|
|
788
|
+
f.write("\n")
|
|
779
789
|
|
|
780
790
|
os.chmod(script_name, 0o755)
|
|
781
791
|
|
|
@@ -830,6 +840,7 @@ def _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, ru
|
|
|
830
840
|
|
|
831
841
|
# TODO: Currently no memory usage tracking in breakpoints, builtins, or unexpected errors.
|
|
832
842
|
max_mem_bytes = 0
|
|
843
|
+
cpu_start = time.time()
|
|
833
844
|
|
|
834
845
|
stdout_file, stderr_file = __get_stdio(chip, tool, task, flow, step, index)
|
|
835
846
|
is_stdout_log = chip.get('tool', tool, 'task', task, 'stdout', 'destination',
|
|
@@ -881,7 +892,10 @@ def _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, ru
|
|
|
881
892
|
|
|
882
893
|
##################
|
|
883
894
|
# Make record of tool options
|
|
884
|
-
|
|
895
|
+
if cmd_args is not None:
|
|
896
|
+
chip.set('record', 'toolargs',
|
|
897
|
+
' '.join(f'"{arg}"' if ' ' in arg else arg for arg in cmd_args),
|
|
898
|
+
step=step, index=index)
|
|
885
899
|
|
|
886
900
|
chip.logger.info('%s', printable_cmd)
|
|
887
901
|
timeout = chip.get('option', 'timeout', step=step, index=index)
|
|
@@ -1003,8 +1017,15 @@ def _run_executable_or_builtin(chip, step, index, version, toolpath, workdir, ru
|
|
|
1003
1017
|
chip.logger.warning(msg)
|
|
1004
1018
|
chip._error = True
|
|
1005
1019
|
|
|
1020
|
+
# Capture cpu runtime
|
|
1021
|
+
record_metric(chip, step, index, 'exetime', round((time.time() - cpu_start), 2),
|
|
1022
|
+
source=None,
|
|
1023
|
+
source_unit='s')
|
|
1024
|
+
|
|
1006
1025
|
# Capture memory usage
|
|
1007
|
-
record_metric(chip, step, index, 'memory', max_mem_bytes,
|
|
1026
|
+
record_metric(chip, step, index, 'memory', max_mem_bytes,
|
|
1027
|
+
source=None,
|
|
1028
|
+
source_unit='B')
|
|
1008
1029
|
|
|
1009
1030
|
|
|
1010
1031
|
def _post_process(chip, step, index):
|
|
@@ -1074,26 +1095,26 @@ def _executenode(chip, step, index, replay):
|
|
|
1074
1095
|
|
|
1075
1096
|
send_messages.send(chip, "skipped", step, index)
|
|
1076
1097
|
else:
|
|
1077
|
-
_set_env_vars(chip, step, index)
|
|
1098
|
+
org_env = _set_env_vars(chip, step, index)
|
|
1078
1099
|
|
|
1079
1100
|
run_func = getattr(chip._get_task_module(step, index, flow=flow), 'run', None)
|
|
1080
|
-
|
|
1101
|
+
toolpath, version = _check_tool_version(chip, step, index, run_func)
|
|
1102
|
+
|
|
1103
|
+
if version:
|
|
1104
|
+
chip.set('record', 'toolversion', version, step=step, index=index)
|
|
1105
|
+
|
|
1106
|
+
if toolpath:
|
|
1107
|
+
chip.set('record', 'toolpath', toolpath, step=step, index=index)
|
|
1081
1108
|
|
|
1082
1109
|
# Write manifest (tool interface) (Don't move this!)
|
|
1083
1110
|
_write_task_manifest(chip, tool)
|
|
1084
1111
|
|
|
1085
1112
|
send_messages.send(chip, "begin", step, index)
|
|
1086
1113
|
|
|
1087
|
-
# Start CPU Timer
|
|
1088
|
-
chip.logger.debug("Starting executable")
|
|
1089
|
-
cpu_start = time.time()
|
|
1090
|
-
|
|
1091
1114
|
_run_executable_or_builtin(chip, step, index, version, toolpath, workdir, run_func)
|
|
1092
1115
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
cputime = round((cpu_end - cpu_start), 2)
|
|
1096
|
-
record_metric(chip, step, index, 'exetime', cputime, source=None, source_unit='s')
|
|
1116
|
+
os.environ.clear()
|
|
1117
|
+
os.environ.update(org_env)
|
|
1097
1118
|
|
|
1098
1119
|
_post_process(chip, step, index)
|
|
1099
1120
|
|
|
@@ -1120,24 +1141,18 @@ def _pre_process(chip, step, index):
|
|
|
1120
1141
|
|
|
1121
1142
|
|
|
1122
1143
|
def _set_env_vars(chip, step, index):
|
|
1123
|
-
|
|
1124
|
-
|
|
1144
|
+
org_env = os.environ.copy()
|
|
1145
|
+
|
|
1146
|
+
tool, task = get_tool_task(chip, step, index)
|
|
1125
1147
|
|
|
1126
1148
|
chip.schema._start_record_access()
|
|
1127
|
-
# License file configuration.
|
|
1128
|
-
for item in chip.getkeys('tool', tool, 'licenseserver'):
|
|
1129
|
-
license_file = chip.get('tool', tool, 'licenseserver', item, step=step, index=index)
|
|
1130
|
-
if license_file:
|
|
1131
|
-
os.environ[item] = ':'.join(license_file)
|
|
1132
1149
|
|
|
1133
|
-
|
|
1134
|
-
for item in chip.getkeys('tool', tool, 'task', task, 'env'):
|
|
1135
|
-
val = chip.get('tool', tool, 'task', task, 'env', item, step=step, index=index)
|
|
1136
|
-
if val:
|
|
1137
|
-
os.environ[item] = val
|
|
1150
|
+
os.environ.update(_get_run_env_vars(chip, tool, task, step, index, include_path=True))
|
|
1138
1151
|
|
|
1139
1152
|
chip.schema._stop_record_access()
|
|
1140
1153
|
|
|
1154
|
+
return org_env
|
|
1155
|
+
|
|
1141
1156
|
|
|
1142
1157
|
def _check_tool_version(chip, step, index, run_func=None):
|
|
1143
1158
|
'''
|
|
@@ -1214,8 +1229,7 @@ def _hash_files(chip, step, index, setup=False):
|
|
|
1214
1229
|
args = item.split(',')
|
|
1215
1230
|
sc_type = chip.get(*args, field='type')
|
|
1216
1231
|
if 'file' in sc_type or 'dir' in sc_type:
|
|
1217
|
-
|
|
1218
|
-
if pernode == 'never':
|
|
1232
|
+
if chip.get(*args, field='pernode').is_never():
|
|
1219
1233
|
if not setup:
|
|
1220
1234
|
if chip.get(*args, field='filehash'):
|
|
1221
1235
|
continue
|
|
@@ -1355,7 +1369,7 @@ def assert_required_accesses(chip, step, index):
|
|
|
1355
1369
|
gets.add(tuple(key))
|
|
1356
1370
|
|
|
1357
1371
|
def get_value(*key):
|
|
1358
|
-
if chip.get(*key, field='pernode')
|
|
1372
|
+
if chip.get(*key, field='pernode').is_never():
|
|
1359
1373
|
return chip.get(*key)
|
|
1360
1374
|
else:
|
|
1361
1375
|
return chip.get(*key, step=step, index=index)
|
|
@@ -1728,19 +1742,6 @@ def get_record_time(chip, step, index, timetype):
|
|
|
1728
1742
|
'%Y-%m-%d %H:%M:%S').timestamp()
|
|
1729
1743
|
|
|
1730
1744
|
|
|
1731
|
-
#######################################
|
|
1732
|
-
def __record_tool(chip, step, index, toolversion=None, toolpath=None, cli_args=None):
|
|
1733
|
-
if toolversion:
|
|
1734
|
-
chip.set('record', 'toolversion', toolversion, step=step, index=index)
|
|
1735
|
-
|
|
1736
|
-
if toolpath:
|
|
1737
|
-
chip.set('record', 'toolpath', toolpath, step=step, index=index)
|
|
1738
|
-
|
|
1739
|
-
if cli_args is not None:
|
|
1740
|
-
toolargs = ' '.join(f'"{arg}"' if ' ' in arg else arg for arg in cli_args)
|
|
1741
|
-
chip.set('record', 'toolargs', toolargs, step=step, index=index)
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
1745
|
#######################################
|
|
1745
1746
|
def _get_cloud_region():
|
|
1746
1747
|
# TODO: add logic to figure out if we're running on a remote cluster and
|
|
@@ -1864,6 +1865,22 @@ def kill_process(chip, proc, tool, poll_interval, msg=""):
|
|
|
1864
1865
|
utils.terminate_process(proc.pid)
|
|
1865
1866
|
|
|
1866
1867
|
|
|
1868
|
+
def get_check_node_keys(chip, step, index):
|
|
1869
|
+
tool, task = get_tool_task(chip, step, index)
|
|
1870
|
+
|
|
1871
|
+
# Collect keys to check for changes
|
|
1872
|
+
required = chip.get('tool', tool, 'task', task, 'require', step=step, index=index)
|
|
1873
|
+
|
|
1874
|
+
tool_task_key = ('tool', tool, 'task', task)
|
|
1875
|
+
for key in ('option', 'threads', 'prescript', 'postscript', 'refdir', 'script',):
|
|
1876
|
+
required.append(",".join([*tool_task_key, key]))
|
|
1877
|
+
|
|
1878
|
+
for env_key in chip.getkeys(*tool_task_key, 'env'):
|
|
1879
|
+
required.append(",".join([*tool_task_key, 'env', env_key]))
|
|
1880
|
+
|
|
1881
|
+
return set(sorted(required))
|
|
1882
|
+
|
|
1883
|
+
|
|
1867
1884
|
def check_node_inputs(chip, step, index):
|
|
1868
1885
|
from siliconcompiler import Chip # import here to avoid circular import
|
|
1869
1886
|
|
|
@@ -1910,16 +1927,15 @@ def check_node_inputs(chip, step, index):
|
|
|
1910
1927
|
if tool != input_tool or task != input_task:
|
|
1911
1928
|
return False
|
|
1912
1929
|
|
|
1913
|
-
#
|
|
1914
|
-
|
|
1915
|
-
|
|
1930
|
+
# Check if inputs changed
|
|
1931
|
+
new_inputs = set(_select_inputs(chip, step, index, trial=True))
|
|
1932
|
+
if set(input_chip.get('record', 'inputnode', step=step, index=index)) != new_inputs:
|
|
1933
|
+
chip.logger.warning(f'inputs to {step}{index} has been modified from previous run')
|
|
1934
|
+
return False
|
|
1916
1935
|
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
for check_chip in (chip, input_chip):
|
|
1921
|
-
for env_key in chip.getkeys(*tool_task_key, 'env'):
|
|
1922
|
-
required.append(",".join([*tool_task_key, 'env', env_key]))
|
|
1936
|
+
# Collect keys to check for changes
|
|
1937
|
+
required = get_check_node_keys(chip, step, index)
|
|
1938
|
+
required.update(get_check_node_keys(input_chip, step, index))
|
|
1923
1939
|
|
|
1924
1940
|
def print_warning(key, extra=None):
|
|
1925
1941
|
if extra:
|
|
@@ -1930,18 +1946,16 @@ def check_node_inputs(chip, step, index):
|
|
|
1930
1946
|
'from previous run')
|
|
1931
1947
|
|
|
1932
1948
|
# Check if keys have been modified
|
|
1933
|
-
for check_key in
|
|
1949
|
+
for check_key in required:
|
|
1934
1950
|
key = check_key.split(',')
|
|
1935
1951
|
|
|
1936
1952
|
if not chip.valid(*key) or not input_chip.valid(*key):
|
|
1937
1953
|
print_warning(key)
|
|
1938
1954
|
return False
|
|
1939
1955
|
|
|
1940
|
-
pernode = chip.get(*key, field='pernode')
|
|
1941
|
-
|
|
1942
1956
|
check_step = step
|
|
1943
1957
|
check_index = index
|
|
1944
|
-
if
|
|
1958
|
+
if chip.get(*key, field='pernode').is_never():
|
|
1945
1959
|
check_step = None
|
|
1946
1960
|
check_index = None
|
|
1947
1961
|
|
|
@@ -2244,7 +2258,7 @@ def _check_manifest_dynamic(chip, step, index):
|
|
|
2244
2258
|
error = True
|
|
2245
2259
|
else:
|
|
2246
2260
|
paramtype = chip.get(*keypath, field='type')
|
|
2247
|
-
is_perstep = chip.get(*keypath, field='pernode')
|
|
2261
|
+
is_perstep = not chip.get(*keypath, field='pernode').is_never()
|
|
2248
2262
|
if ('file' in paramtype) or ('dir' in paramtype):
|
|
2249
2263
|
for val, check_step, check_index in chip.schema._getvals(*keypath):
|
|
2250
2264
|
if is_perstep:
|
|
@@ -2298,7 +2312,7 @@ def _clear_record(chip, step, index, record, preserve=None):
|
|
|
2298
2312
|
if preserve and record in preserve:
|
|
2299
2313
|
return
|
|
2300
2314
|
|
|
2301
|
-
if chip.get('record', record, field='pernode')
|
|
2315
|
+
if chip.get('record', record, field='pernode').is_never():
|
|
2302
2316
|
chip.unset('record', record)
|
|
2303
2317
|
else:
|
|
2304
2318
|
chip.unset('record', record, step=step, index=index)
|