siliconcompiler 0.35.2__py3-none-any.whl → 0.35.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. siliconcompiler/_metadata.py +1 -1
  2. siliconcompiler/apps/sc_issue.py +18 -2
  3. siliconcompiler/apps/smake.py +106 -100
  4. siliconcompiler/checklist.py +2 -1
  5. siliconcompiler/constraints/asic_component.py +49 -11
  6. siliconcompiler/constraints/asic_floorplan.py +23 -21
  7. siliconcompiler/constraints/asic_pins.py +55 -17
  8. siliconcompiler/constraints/asic_timing.py +53 -22
  9. siliconcompiler/constraints/fpga_timing.py +5 -6
  10. siliconcompiler/data/templates/replay/replay.sh.j2 +27 -14
  11. siliconcompiler/flowgraph.py +418 -129
  12. siliconcompiler/library.py +5 -4
  13. siliconcompiler/package/__init__.py +17 -6
  14. siliconcompiler/package/https.py +10 -5
  15. siliconcompiler/project.py +92 -33
  16. siliconcompiler/remote/client.py +17 -6
  17. siliconcompiler/scheduler/docker.py +24 -25
  18. siliconcompiler/scheduler/scheduler.py +284 -121
  19. siliconcompiler/scheduler/schedulernode.py +196 -90
  20. siliconcompiler/scheduler/slurm.py +113 -29
  21. siliconcompiler/scheduler/taskscheduler.py +0 -7
  22. siliconcompiler/schema/__init__.py +3 -2
  23. siliconcompiler/schema/_metadata.py +1 -1
  24. siliconcompiler/schema/baseschema.py +205 -93
  25. siliconcompiler/schema/editableschema.py +29 -0
  26. siliconcompiler/schema/namedschema.py +21 -13
  27. siliconcompiler/schema/parametervalue.py +14 -2
  28. siliconcompiler/schema/safeschema.py +18 -7
  29. siliconcompiler/schema_support/dependencyschema.py +4 -3
  30. siliconcompiler/schema_support/option.py +82 -1
  31. siliconcompiler/schema_support/pathschema.py +14 -15
  32. siliconcompiler/schema_support/record.py +5 -4
  33. siliconcompiler/targets/asap7_demo.py +4 -1
  34. siliconcompiler/tool.py +56 -29
  35. siliconcompiler/tools/builtin/__init__.py +2 -0
  36. siliconcompiler/tools/builtin/filter.py +8 -1
  37. siliconcompiler/tools/builtin/importfiles.py +2 -0
  38. siliconcompiler/tools/klayout/__init__.py +3 -0
  39. siliconcompiler/tools/klayout/scripts/klayout_convert_drc_db.py +1 -0
  40. siliconcompiler/tools/klayout/scripts/klayout_export.py +1 -0
  41. siliconcompiler/tools/klayout/scripts/klayout_operations.py +1 -0
  42. siliconcompiler/tools/klayout/scripts/klayout_show.py +2 -1
  43. siliconcompiler/tools/klayout/scripts/klayout_utils.py +3 -4
  44. siliconcompiler/tools/klayout/show.py +17 -5
  45. siliconcompiler/tools/openroad/__init__.py +27 -1
  46. siliconcompiler/tools/openroad/_apr.py +81 -4
  47. siliconcompiler/tools/openroad/clock_tree_synthesis.py +1 -0
  48. siliconcompiler/tools/openroad/global_placement.py +1 -0
  49. siliconcompiler/tools/openroad/init_floorplan.py +116 -7
  50. siliconcompiler/tools/openroad/power_grid_analysis.py +174 -0
  51. siliconcompiler/tools/openroad/repair_design.py +1 -0
  52. siliconcompiler/tools/openroad/repair_timing.py +1 -0
  53. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +1 -1
  54. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +42 -4
  55. siliconcompiler/tools/openroad/scripts/apr/sc_irdrop.tcl +146 -0
  56. siliconcompiler/tools/openroad/scripts/apr/sc_repair_design.tcl +1 -1
  57. siliconcompiler/tools/openroad/scripts/apr/sc_write_data.tcl +4 -6
  58. siliconcompiler/tools/openroad/scripts/common/procs.tcl +1 -1
  59. siliconcompiler/tools/openroad/scripts/common/reports.tcl +1 -1
  60. siliconcompiler/tools/openroad/scripts/rcx/sc_rcx_bench.tcl +2 -4
  61. siliconcompiler/tools/opensta/__init__.py +1 -1
  62. siliconcompiler/tools/opensta/scripts/sc_timing.tcl +17 -12
  63. siliconcompiler/tools/vivado/scripts/sc_bitstream.tcl +11 -0
  64. siliconcompiler/tools/vivado/scripts/sc_place.tcl +11 -0
  65. siliconcompiler/tools/vivado/scripts/sc_route.tcl +11 -0
  66. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +10 -0
  67. siliconcompiler/tools/vpr/__init__.py +28 -0
  68. siliconcompiler/tools/yosys/prepareLib.py +7 -2
  69. siliconcompiler/tools/yosys/scripts/sc_screenshot.tcl +1 -1
  70. siliconcompiler/tools/yosys/scripts/sc_synth_asic.tcl +40 -4
  71. siliconcompiler/tools/yosys/scripts/sc_synth_fpga.tcl +15 -5
  72. siliconcompiler/tools/yosys/syn_asic.py +62 -2
  73. siliconcompiler/tools/yosys/syn_fpga.py +8 -0
  74. siliconcompiler/toolscripts/_tools.json +6 -6
  75. siliconcompiler/utils/__init__.py +243 -51
  76. siliconcompiler/utils/curation.py +89 -56
  77. siliconcompiler/utils/issue.py +6 -1
  78. siliconcompiler/utils/multiprocessing.py +35 -2
  79. siliconcompiler/utils/paths.py +21 -0
  80. siliconcompiler/utils/settings.py +141 -0
  81. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/METADATA +5 -4
  82. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/RECORD +86 -83
  83. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/WHEEL +0 -0
  84. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/entry_points.txt +0 -0
  85. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/licenses/LICENSE +0 -0
  86. {siliconcompiler-0.35.2.dist-info → siliconcompiler-0.35.4.dist-info}/top_level.txt +0 -0
@@ -5,15 +5,18 @@ import shutil
5
5
  import stat
6
6
  import subprocess
7
7
  import uuid
8
+ import time
8
9
 
9
10
  import os.path
10
11
 
11
- from siliconcompiler import utils
12
- from siliconcompiler.utils.curation import collect
13
- from siliconcompiler.utils.paths import collectiondir, jobdir
12
+ from typing import List, Union, Final
13
+
14
+ from siliconcompiler import utils, sc_open
15
+ from siliconcompiler.utils.paths import jobdir
14
16
  from siliconcompiler.package import RemoteResolver
15
- from siliconcompiler.flowgraph import RuntimeFlowgraph
16
17
  from siliconcompiler.scheduler import SchedulerNode
18
+ from siliconcompiler.utils.logging import SCBlankLoggerFormatter
19
+ from siliconcompiler.utils.multiprocessing import MPManager
17
20
 
18
21
 
19
22
  class SlurmSchedulerNode(SchedulerNode):
@@ -24,6 +27,10 @@ class SlurmSchedulerNode(SchedulerNode):
24
27
  It prepares a run script, a manifest, and uses the 'srun' command
25
28
  to execute the step on a compute node.
26
29
  """
30
+ __OPTIONS: Final[str] = "scheduler-slurm"
31
+
32
+ _MAX_FS_DELAY = 2
33
+ _FS_DWELL = 0.1
27
34
 
28
35
  def __init__(self, project, step, index, replay=False):
29
36
  """Initializes a SlurmSchedulerNode.
@@ -52,34 +59,31 @@ class SlurmSchedulerNode(SchedulerNode):
52
59
  """
53
60
  A static pre-processing hook for the Slurm scheduler.
54
61
 
55
- This method checks if the compilation flow starts from an entry node.
56
- If so, it calls :meth:`.collect()` to gather all necessary source files
57
- into a central location before any remote jobs are submitted. This
58
- ensures that compute nodes have access to all required source files.
62
+ This method ensures that the Slurm environment is available and loads
63
+ any existing user configuration for the scheduler.
59
64
 
60
65
  Args:
61
66
  project (Project): The project object to perform pre-processing on.
62
67
  """
63
- if os.path.exists(collectiondir(project)):
64
- # nothing to do
65
- return
66
-
67
- do_collect = False
68
- flow = project.get('option', 'flow')
69
- entry_nodes = project.get("flowgraph", flow, field="schema").get_entry_nodes()
68
+ SlurmSchedulerNode.assert_slurm()
70
69
 
71
- runtime = RuntimeFlowgraph(
72
- project.get("flowgraph", flow, field='schema'),
73
- from_steps=project.get('option', 'from'),
74
- to_steps=project.get('option', 'to'),
75
- prune_nodes=project.get('option', 'prune'))
70
+ @staticmethod
71
+ def _set_user_config(tag: str, value: Union[List[str], str]) -> None:
72
+ """
73
+ Sets a specific value in the user configuration map.
76
74
 
77
- for (step, index) in runtime.get_nodes():
78
- if (step, index) in entry_nodes:
79
- do_collect = True
75
+ Args:
76
+ tag (str): The configuration key to update.
77
+ value (Union[List[str], str]): The value to assign to the key.
78
+ """
79
+ MPManager.get_settings().set(SlurmSchedulerNode.__OPTIONS, tag, value)
80
80
 
81
- if do_collect:
82
- collect(project)
81
+ @staticmethod
82
+ def _write_user_config() -> None:
83
+ """
84
+ Writes the current system configuration to the user configuration file.
85
+ """
86
+ MPManager.get_settings().save()
83
87
 
84
88
  @property
85
89
  def is_local(self):
@@ -150,6 +154,49 @@ class SlurmSchedulerNode(SchedulerNode):
150
154
  # Return the first listed partition
151
155
  return sinfo['nodes'][0]['partitions'][0]
152
156
 
157
+ @staticmethod
158
+ def assert_slurm() -> None:
159
+ """
160
+ Check if slurm is installed and throw error when not installed.
161
+ """
162
+ if shutil.which('sinfo') is None:
163
+ raise RuntimeError('slurm is not available or installed on this machine')
164
+
165
+ def mark_copy(self) -> bool:
166
+ sharedprefix: List[str] = MPManager.get_settings().get(
167
+ SlurmSchedulerNode.__OPTIONS, "sharedpaths", default=[])
168
+
169
+ if "/" in sharedprefix:
170
+ # Entire filesystem is shared so no need to check
171
+ return False
172
+
173
+ do_collect = False
174
+ for key in self.get_required_path_keys():
175
+ mark_copy = True
176
+ if sharedprefix:
177
+ mark_copy = False
178
+
179
+ check_step, check_index = self.step, self.index
180
+ if self.project.get(*key, field='pernode').is_never():
181
+ check_step, check_index = None, None
182
+
183
+ paths = self.project.find_files(*key, missing_ok=True,
184
+ step=check_step, index=check_index)
185
+ if not isinstance(paths, list):
186
+ paths = [paths]
187
+ paths = [str(path) for path in paths if path]
188
+
189
+ for path in paths:
190
+ if not any([path.startswith(shared) for shared in sharedprefix]):
191
+ # File exists outside shared paths and needs to be copied
192
+ mark_copy = True
193
+ break
194
+
195
+ if mark_copy:
196
+ self.project.set(*key, True, field='copy')
197
+ do_collect = True
198
+ return do_collect
199
+
153
200
  def run(self):
154
201
  """
155
202
  Runs the node's task as a job on a Slurm cluster.
@@ -161,9 +208,6 @@ class SlurmSchedulerNode(SchedulerNode):
161
208
 
162
209
  self._init_run_logger()
163
210
 
164
- if shutil.which('sinfo') is None:
165
- raise RuntimeError('slurm is not available or installed on this machine')
166
-
167
211
  # Determine which cluster parititon to use.
168
212
  partition = self.project.get('option', 'scheduler', 'queue',
169
213
  step=self.step, index=self.index)
@@ -191,7 +235,7 @@ class SlurmSchedulerNode(SchedulerNode):
191
235
  with open(script_file, 'w') as sf:
192
236
  sf.write(utils.get_file_template('slurm/run.sh').render(
193
237
  cfg_file=shlex.quote(cfg_file),
194
- build_dir=shlex.quote(self.project.get("option", "builddir")),
238
+ build_dir=shlex.quote(self.project.option.get_builddir()),
195
239
  step=shlex.quote(self.step),
196
240
  index=shlex.quote(self.index),
197
241
  cachedir=shlex.quote(str(RemoteResolver.determine_cache_dir(self.project)))
@@ -217,9 +261,12 @@ class SlurmSchedulerNode(SchedulerNode):
217
261
 
218
262
  schedule_cmd.append(script_file)
219
263
 
264
+ self.logger.debug(f"Executing slurm command: {shlex.join(schedule_cmd)}")
265
+
220
266
  # Run the 'srun' command, and track its output.
221
267
  # TODO: output should be fed to log, and stdout if quiet = False
222
268
  step_result = subprocess.Popen(schedule_cmd,
269
+ stdin=subprocess.DEVNULL,
223
270
  stdout=subprocess.PIPE,
224
271
  stderr=subprocess.STDOUT)
225
272
 
@@ -227,3 +274,40 @@ class SlurmSchedulerNode(SchedulerNode):
227
274
  # as it has closed its output stream. But if we don't call '.wait()',
228
275
  # the '.returncode' value will not be set correctly.
229
276
  step_result.wait()
277
+
278
+ # Attempt to list dir to trigger network FS to update
279
+ try:
280
+ os.listdir(os.path.dirname(log_file))
281
+ except: # noqa E722
282
+ pass
283
+
284
+ # Print the log to logger
285
+ if os.path.exists(log_file):
286
+ org_formatter = self.project._logger_console.formatter
287
+ try:
288
+ self.project._logger_console.setFormatter(SCBlankLoggerFormatter())
289
+ with sc_open(log_file) as log:
290
+ for line in log.readlines():
291
+ self.logger.info(line.rstrip())
292
+ finally:
293
+ self.project._logger_console.setFormatter(org_formatter)
294
+
295
+ if step_result.returncode != 0:
296
+ self.logger.error(f"Slurm exited with a non-zero code ({step_result.returncode}).")
297
+ if os.path.exists(log_file):
298
+ self.logger.error(f"Node log file: {log_file}")
299
+ self.halt()
300
+
301
+ # Wait for manifest to propagate through network filesystem
302
+ start = time.time()
303
+ elapsed = 0
304
+ manifest_path = self.get_manifest()
305
+ while not os.path.exists(manifest_path) and elapsed <= SlurmSchedulerNode._MAX_FS_DELAY:
306
+ os.listdir(os.path.dirname(manifest_path))
307
+ elapsed = time.time() - start
308
+ time.sleep(SlurmSchedulerNode._FS_DWELL)
309
+ if not os.path.exists(manifest_path):
310
+ self.logger.error(f"Manifest was not created on time: {manifest_path}")
311
+
312
+ def check_required_paths(self) -> bool:
313
+ return True
@@ -117,8 +117,6 @@ class TaskScheduler:
117
117
  from_steps=set([step for step, _ in self.__flow.get_entry_nodes()]),
118
118
  prune_nodes=self.__project.option.get_prune())
119
119
 
120
- init_funcs = set()
121
-
122
120
  for step, index in self.__runtime_flow.get_nodes():
123
121
  if self.__record.get('status', step=step, index=index) != NodeStatus.PENDING:
124
122
  continue
@@ -145,7 +143,6 @@ class TaskScheduler:
145
143
  task["node"].set_queue(pipe, self.__log_queue)
146
144
 
147
145
  task["proc"] = multiprocessing.Process(target=task["node"].run)
148
- init_funcs.add(task["node"].init)
149
146
  self.__nodes[(step, index)] = task
150
147
 
151
148
  # Create ordered list of nodes
@@ -155,10 +152,6 @@ class TaskScheduler:
155
152
  if node in self.__nodes:
156
153
  self.__ordered_nodes.append(node)
157
154
 
158
- # Call preprocessing for schedulers
159
- for init_func in init_funcs:
160
- init_func(self.__project)
161
-
162
155
  def run(self, job_log_handler: logging.Handler) -> None:
163
156
  """
164
157
  The main entry point for the task scheduling loop.
@@ -4,7 +4,7 @@ from .parameter import Parameter, Scope, PerNode
4
4
  from .journal import Journal
5
5
  from .safeschema import SafeSchema
6
6
  from .editableschema import EditableSchema
7
- from .baseschema import BaseSchema
7
+ from .baseschema import BaseSchema, LazyLoad
8
8
  from .namedschema import NamedSchema
9
9
  from .docschema import DocsSchema
10
10
 
@@ -17,7 +17,8 @@ __all__ = [
17
17
  "Scope",
18
18
  "PerNode",
19
19
  "Journal",
20
- "DocsSchema"
20
+ "DocsSchema",
21
+ "LazyLoad"
21
22
  ]
22
23
 
23
24
  SCHEMA_VERSION = __version__
@@ -1,2 +1,2 @@
1
1
  # Version number following semver standard.
2
- version = '0.52.0'
2
+ version = '0.52.1'