siliconcompiler 0.34.2__py3-none-any.whl → 0.34.3__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 (121) hide show
  1. siliconcompiler/__init__.py +12 -5
  2. siliconcompiler/__main__.py +1 -7
  3. siliconcompiler/_metadata.py +1 -1
  4. siliconcompiler/apps/_common.py +104 -23
  5. siliconcompiler/apps/sc.py +4 -8
  6. siliconcompiler/apps/sc_dashboard.py +6 -4
  7. siliconcompiler/apps/sc_install.py +10 -6
  8. siliconcompiler/apps/sc_issue.py +7 -5
  9. siliconcompiler/apps/sc_remote.py +1 -1
  10. siliconcompiler/apps/sc_server.py +9 -14
  11. siliconcompiler/apps/sc_show.py +6 -5
  12. siliconcompiler/apps/smake.py +130 -94
  13. siliconcompiler/apps/utils/replay.py +4 -7
  14. siliconcompiler/apps/utils/summarize.py +3 -5
  15. siliconcompiler/asic.py +420 -0
  16. siliconcompiler/checklist.py +25 -2
  17. siliconcompiler/cmdlineschema.py +534 -0
  18. siliconcompiler/constraints/asic_component.py +2 -2
  19. siliconcompiler/constraints/asic_pins.py +2 -2
  20. siliconcompiler/constraints/asic_timing.py +3 -3
  21. siliconcompiler/core.py +7 -32
  22. siliconcompiler/data/templates/tcl/manifest.tcl.j2 +8 -0
  23. siliconcompiler/dependencyschema.py +89 -31
  24. siliconcompiler/design.py +176 -207
  25. siliconcompiler/filesetschema.py +250 -0
  26. siliconcompiler/flowgraph.py +274 -95
  27. siliconcompiler/fpga.py +124 -1
  28. siliconcompiler/library.py +218 -20
  29. siliconcompiler/metric.py +233 -20
  30. siliconcompiler/package/__init__.py +271 -50
  31. siliconcompiler/package/git.py +92 -16
  32. siliconcompiler/package/github.py +108 -12
  33. siliconcompiler/package/https.py +79 -16
  34. siliconcompiler/packageschema.py +88 -7
  35. siliconcompiler/pathschema.py +31 -2
  36. siliconcompiler/pdk.py +566 -1
  37. siliconcompiler/project.py +1095 -94
  38. siliconcompiler/record.py +38 -1
  39. siliconcompiler/remote/__init__.py +5 -2
  40. siliconcompiler/remote/client.py +11 -6
  41. siliconcompiler/remote/schema.py +5 -23
  42. siliconcompiler/remote/server.py +41 -54
  43. siliconcompiler/report/__init__.py +3 -3
  44. siliconcompiler/report/dashboard/__init__.py +48 -14
  45. siliconcompiler/report/dashboard/cli/__init__.py +99 -21
  46. siliconcompiler/report/dashboard/cli/board.py +364 -179
  47. siliconcompiler/report/dashboard/web/__init__.py +90 -12
  48. siliconcompiler/report/dashboard/web/components/__init__.py +219 -240
  49. siliconcompiler/report/dashboard/web/components/flowgraph.py +49 -26
  50. siliconcompiler/report/dashboard/web/components/graph.py +139 -100
  51. siliconcompiler/report/dashboard/web/layouts/__init__.py +29 -1
  52. siliconcompiler/report/dashboard/web/layouts/_common.py +38 -2
  53. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph.py +39 -26
  54. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_node_tab.py +50 -50
  55. siliconcompiler/report/dashboard/web/layouts/vertical_flowgraph_sac_tabs.py +49 -46
  56. siliconcompiler/report/dashboard/web/state.py +141 -14
  57. siliconcompiler/report/dashboard/web/utils/__init__.py +79 -16
  58. siliconcompiler/report/dashboard/web/utils/file_utils.py +74 -11
  59. siliconcompiler/report/dashboard/web/viewer.py +25 -1
  60. siliconcompiler/report/report.py +5 -2
  61. siliconcompiler/report/summary_image.py +29 -11
  62. siliconcompiler/scheduler/__init__.py +9 -1
  63. siliconcompiler/scheduler/docker.py +79 -1
  64. siliconcompiler/scheduler/run_node.py +35 -19
  65. siliconcompiler/scheduler/scheduler.py +208 -24
  66. siliconcompiler/scheduler/schedulernode.py +372 -46
  67. siliconcompiler/scheduler/send_messages.py +77 -29
  68. siliconcompiler/scheduler/slurm.py +76 -12
  69. siliconcompiler/scheduler/taskscheduler.py +140 -20
  70. siliconcompiler/schema/__init__.py +0 -2
  71. siliconcompiler/schema/baseschema.py +194 -38
  72. siliconcompiler/schema/journal.py +7 -4
  73. siliconcompiler/schema/namedschema.py +16 -10
  74. siliconcompiler/schema/parameter.py +55 -9
  75. siliconcompiler/schema/parametervalue.py +60 -0
  76. siliconcompiler/schema/safeschema.py +25 -2
  77. siliconcompiler/schema/schema_cfg.py +5 -5
  78. siliconcompiler/schema/utils.py +2 -2
  79. siliconcompiler/schema_obj.py +20 -3
  80. siliconcompiler/tool.py +979 -302
  81. siliconcompiler/tools/bambu/__init__.py +41 -0
  82. siliconcompiler/tools/builtin/concatenate.py +2 -2
  83. siliconcompiler/tools/builtin/minimum.py +2 -1
  84. siliconcompiler/tools/builtin/mux.py +2 -1
  85. siliconcompiler/tools/builtin/nop.py +2 -1
  86. siliconcompiler/tools/builtin/verify.py +2 -1
  87. siliconcompiler/tools/klayout/__init__.py +95 -0
  88. siliconcompiler/tools/openroad/__init__.py +289 -0
  89. siliconcompiler/tools/openroad/scripts/apr/preamble.tcl +3 -0
  90. siliconcompiler/tools/openroad/scripts/apr/sc_detailed_route.tcl +7 -2
  91. siliconcompiler/tools/openroad/scripts/apr/sc_global_route.tcl +8 -4
  92. siliconcompiler/tools/openroad/scripts/apr/sc_init_floorplan.tcl +9 -5
  93. siliconcompiler/tools/openroad/scripts/common/write_images.tcl +5 -1
  94. siliconcompiler/tools/slang/__init__.py +1 -1
  95. siliconcompiler/tools/slang/elaborate.py +2 -1
  96. siliconcompiler/tools/vivado/scripts/sc_run.tcl +1 -1
  97. siliconcompiler/tools/vivado/scripts/sc_syn_fpga.tcl +8 -1
  98. siliconcompiler/tools/vivado/syn_fpga.py +6 -0
  99. siliconcompiler/tools/vivado/vivado.py +35 -2
  100. siliconcompiler/tools/vpr/__init__.py +150 -0
  101. siliconcompiler/tools/yosys/__init__.py +369 -1
  102. siliconcompiler/tools/yosys/scripts/procs.tcl +0 -1
  103. siliconcompiler/toolscripts/_tools.json +5 -10
  104. siliconcompiler/utils/__init__.py +66 -0
  105. siliconcompiler/utils/flowgraph.py +2 -2
  106. siliconcompiler/utils/issue.py +2 -1
  107. siliconcompiler/utils/logging.py +14 -0
  108. siliconcompiler/utils/multiprocessing.py +256 -0
  109. siliconcompiler/utils/showtools.py +10 -0
  110. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/METADATA +5 -5
  111. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/RECORD +115 -118
  112. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/entry_points.txt +3 -0
  113. siliconcompiler/schema/cmdlineschema.py +0 -250
  114. siliconcompiler/toolscripts/rhel8/install-slang.sh +0 -40
  115. siliconcompiler/toolscripts/rhel9/install-slang.sh +0 -40
  116. siliconcompiler/toolscripts/ubuntu20/install-slang.sh +0 -47
  117. siliconcompiler/toolscripts/ubuntu22/install-slang.sh +0 -37
  118. siliconcompiler/toolscripts/ubuntu24/install-slang.sh +0 -37
  119. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/WHEEL +0 -0
  120. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/licenses/LICENSE +0 -0
  121. {siliconcompiler-0.34.2.dist-info → siliconcompiler-0.34.3.dist-info}/top_level.txt +0 -0
@@ -110,7 +110,7 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
110
110
  def _is_leaf(cfg):
111
111
  # 'shorthelp' chosen arbitrarily: any mandatory field with a consistent
112
112
  # type would work.
113
- return 'shorthelp' in cfg and isinstance(cfg['shorthelp'], str)
113
+ return 'type' in cfg and isinstance(cfg['type'], str)
114
114
 
115
115
  def build_leaf(manifest_subsect):
116
116
  if PerNode(manifest_subsect['pernode']) == PerNode.NEVER:
@@ -161,7 +161,10 @@ def make_manifest_helper(manifest_subsect, modified_manifest_subsect):
161
161
  else:
162
162
  modified_manifest_subsect[step + index] = value
163
163
 
164
- for key, key_dict in manifest_subsect.items():
164
+ for key, key_dict in sorted(manifest_subsect.items(), key=lambda k: k[0]):
165
+ if key == "__meta__" or key == "__journal__":
166
+ continue
167
+
165
168
  if key != 'default':
166
169
  if _is_leaf(key_dict):
167
170
  modified_manifest_subsect[key] = build_leaf(key_dict)
@@ -1,7 +1,11 @@
1
1
  import os
2
2
  import string
3
+
4
+ import os.path
5
+
3
6
  from PIL import Image, ImageFont, ImageDraw
4
7
 
8
+ import siliconcompiler
5
9
  from siliconcompiler.utils import units
6
10
  from siliconcompiler.report.utils import _find_summary_image, _find_summary_metrics
7
11
 
@@ -12,10 +16,6 @@ def _generate_summary_image(chip, output_path):
12
16
  featuring a layout thumbnail and several metrics.
13
17
  '''
14
18
 
15
- img_path = _find_summary_image(chip)
16
- if not img_path:
17
- return
18
-
19
19
  # Extract metrics for display
20
20
  metrics = {
21
21
  'Chip': chip.design,
@@ -46,14 +46,31 @@ def _generate_summary_image(chip, output_path):
46
46
  'LUTs': ('luts', None),
47
47
  'Fmax': ('fmax', format_freq)}))
48
48
 
49
- # Generate design
49
+ info = []
50
+ for metric, value in metrics.items():
51
+ info.append((metric, value))
52
+
53
+ generate_summary_image(chip, output_path, info)
50
54
 
55
+
56
+ def generate_summary_image(project, output_path, info):
57
+ '''
58
+ Takes a layout screenshot and generates a design summary image
59
+ featuring a layout thumbnail and several metrics.
60
+ '''
61
+
62
+ img_path = _find_summary_image(project)
63
+ if not img_path:
64
+ return
65
+
66
+ # Generate design
51
67
  WIDTH = 1024
52
68
  BORDER = 32
53
69
  LINE_SPACING = 8
54
70
  TEXT_INDENT = 16
55
71
 
56
- FONT_PATH = os.path.join(chip.scroot, 'data', 'RobotoMono', 'RobotoMono-Regular.ttf')
72
+ FONT_PATH = os.path.join(os.path.dirname(siliconcompiler.__file__),
73
+ 'data', 'RobotoMono', 'RobotoMono-Regular.ttf')
57
74
  FONT_SIZE = 40
58
75
 
59
76
  # matches dark gray background color configured in klayout_show.py
@@ -82,8 +99,9 @@ def _generate_summary_image(chip, output_path):
82
99
  text = []
83
100
  x = BORDER + TEXT_INDENT
84
101
  y = thumbnail_height + 2 * BORDER
85
- for metric, value in metrics.items():
86
- line = f'{metric}: {value}'
102
+ max_title_len = max([len(title) for title, _ in info])
103
+ for title, value in info:
104
+ line = f'{title:<{max_title_len}}: {value}'
87
105
 
88
106
  # shorten line till it fits
89
107
  cropped_line = line
@@ -94,8 +112,8 @@ def _generate_summary_image(chip, output_path):
94
112
  cropped_line = cropped_line[:-1]
95
113
 
96
114
  if cropped_line != line:
97
- chip.logger.warning(f'Cropped {line} to {cropped_line} to fit in design summary '
98
- 'image')
115
+ project.logger.warning(f'Cropped {line} to {cropped_line} to fit in design summary '
116
+ 'image')
99
117
 
100
118
  # Stash line to write and coords to write it at
101
119
  text.append(((x, y), cropped_line))
@@ -110,7 +128,7 @@ def _generate_summary_image(chip, output_path):
110
128
  draw.text(coords, line, TEXT_COLOR, font=font)
111
129
 
112
130
  design_summary.save(output_path)
113
- chip.logger.info(f'Generated summary image at {output_path}')
131
+ project.logger.info(f'Generated summary image at {output_path}')
114
132
 
115
133
 
116
134
  def _open_summary_image(image):
@@ -1,5 +1,13 @@
1
+ from siliconcompiler.scheduler.schedulernode import SchedulerNode
2
+ from siliconcompiler.scheduler.slurm import SlurmSchedulerNode
3
+ from siliconcompiler.scheduler.docker import DockerSchedulerNode
1
4
  from siliconcompiler.scheduler.taskscheduler import TaskScheduler
5
+ from siliconcompiler.scheduler.scheduler import Scheduler
2
6
 
3
7
  __all__ = [
4
- "TaskScheduler"
8
+ "Scheduler",
9
+ "SchedulerNode",
10
+ "TaskScheduler",
11
+ "DockerSchedulerNode",
12
+ "SlurmSchedulerNode"
5
13
  ]
@@ -7,10 +7,25 @@ from pathlib import Path
7
7
 
8
8
  from siliconcompiler.package import RemoteResolver
9
9
  from siliconcompiler.utils import default_email_credentials_file
10
- from siliconcompiler.scheduler.schedulernode import SchedulerNode
10
+ from siliconcompiler.scheduler import SchedulerNode
11
11
 
12
12
 
13
13
  def get_image(chip, step, index):
14
+ """Determines the Docker image to use for a given node.
15
+
16
+ The image is selected based on the following priority:
17
+ 1. The value of ['option', 'scheduler', 'queue'] specific to the step/index.
18
+ 2. The value of the 'SC_DOCKER_IMAGE' environment variable.
19
+ 3. A default image name constructed as 'ghcr.io/siliconcompiler/sc_runner:v<version>'.
20
+
21
+ Args:
22
+ chip (Chip): The Chip object.
23
+ step (str): The step name of the node.
24
+ index (str): The index of the node.
25
+
26
+ Returns:
27
+ str: The name of the Docker image to use.
28
+ """
14
29
  from siliconcompiler import __version__
15
30
 
16
31
  queue = chip.get('option', 'scheduler', 'queue', step=step, index=index)
@@ -23,6 +38,27 @@ def get_image(chip, step, index):
23
38
 
24
39
 
25
40
  def get_volumes_directories(chip, cache_dir, workdir, step, index):
41
+ """
42
+ Identifies and categorizes all host directories that need to be mounted
43
+ into the Docker container.
44
+
45
+ This function scans the chip schema for all file and directory paths,
46
+ collects them, and then prunes the list to a minimal set of parent
47
+ directories to mount. It then separates these directories into read-write
48
+ (RW) and read-only (RO) sets.
49
+
50
+ Args:
51
+ chip (Chip): The Chip object.
52
+ cache_dir (str): The path to the cache directory.
53
+ workdir (str): The path to the node's working directory.
54
+ step (str): The step name of the current node.
55
+ index (str): The index of the current node.
56
+
57
+ Returns:
58
+ tuple: A tuple containing two sets: (rw_volumes, ro_volumes).
59
+ `rw_volumes` is a set of Path objects for read-write directories.
60
+ `ro_volumes` is a set of Path objects for read-only directories.
61
+ """
26
62
  all_dirs = set()
27
63
  # Collect files
28
64
  for key in chip.allkeys():
@@ -89,17 +125,46 @@ def get_volumes_directories(chip, cache_dir, workdir, step, index):
89
125
 
90
126
 
91
127
  class DockerSchedulerNode(SchedulerNode):
128
+ """A SchedulerNode implementation for running tasks in a Docker container.
129
+
130
+ This class extends the base SchedulerNode to handle the specifics of
131
+ running a compilation step inside a Docker container. It uses the `docker-py`
132
+ library to manage the container lifecycle, including pulling the image,
133
+
134
+ mounting volumes, and executing the command.
135
+ """
136
+
92
137
  def __init__(self, chip, step, index, replay=False):
138
+ """Initializes a DockerSchedulerNode.
139
+
140
+ Args:
141
+ chip (Chip): The parent Chip object.
142
+ step (str): The step name in the flowgraph.
143
+ index (str): The index for the step.
144
+ replay (bool): If True, sets up the node to replay a previous run.
145
+ """
93
146
  super().__init__(chip, step, index, replay=replay)
94
147
 
95
148
  self.__queue = get_image(self.chip, self.step, self.index)
96
149
 
97
150
  @property
98
151
  def queue(self):
152
+ """str: The Docker image name to be used for the container."""
99
153
  return self.__queue
100
154
 
101
155
  @staticmethod
102
156
  def init(chip):
157
+ """
158
+ A static pre-processing hook for the Docker scheduler.
159
+
160
+ On Windows, this method forces all file/directory parameters to be
161
+ copied rather than linked, which avoids issues with differing
162
+ filesystem types between the host and the Linux-based container.
163
+ It then triggers `chip.collect()` to ensure all files are staged.
164
+
165
+ Args:
166
+ chip (Chip): The Chip object to perform pre-processing on.
167
+ """
103
168
  if sys.platform == 'win32':
104
169
  # this avoids the issue of different file system types
105
170
  chip.logger.error('Setting copy field to true for docker run on Windows')
@@ -112,6 +177,19 @@ class DockerSchedulerNode(SchedulerNode):
112
177
  chip.collect()
113
178
 
114
179
  def run(self):
180
+ """
181
+ Runs the node's task inside a Docker container.
182
+
183
+ This method orchestrates the entire process:
184
+ 1. Connects to the Docker daemon.
185
+ 2. Pulls the required Docker image if it's not present locally.
186
+ 3. Determines and prepares all necessary volume mounts.
187
+ 4. Creates and starts a detached Docker container.
188
+ 5. Writes the current manifest to a file accessible by the container.
189
+ 6. Executes the `sc-node` command inside the container.
190
+ 7. Streams the container's log output to the console.
191
+ 8. Halts on error and ensures the container is stopped upon completion.
192
+ """
115
193
  self._init_run_logger()
116
194
 
117
195
  try:
@@ -1,4 +1,13 @@
1
1
  #!/usr/bin/env python3
2
+ """
3
+ A command-line utility to execute a single node (step and index) from a
4
+ SiliconCompiler flowgraph.
5
+
6
+ This script is designed to be called by a scheduler (like Slurm, Docker, or
7
+ a local process manager) to run a specific task in isolation. It takes all
8
+ necessary configuration information via command-line arguments, sets up a
9
+ Chip object, and executes the specified node's `run()` method.
10
+ """
2
11
 
3
12
  import argparse
4
13
  import os
@@ -8,19 +17,22 @@ import os.path
8
17
 
9
18
  from siliconcompiler import Chip, Schema
10
19
  from siliconcompiler.package import Resolver
11
- from siliconcompiler.scheduler.schedulernode import SchedulerNode
20
+ from siliconcompiler.scheduler import SchedulerNode
12
21
  from siliconcompiler import __version__
13
22
 
14
23
 
15
24
  ##########################
16
25
  def main():
26
+ """The main entry point for the run_node script."""
17
27
  schema = Schema()
18
28
 
19
- # Can't use chip.cmdline because we don't want a bunch of extra logger information
29
+ # Set up a minimal argument parser, as we don't need the full
30
+ # Chip.cmdline() functionality which includes extra logger setup.
20
31
  parser = argparse.ArgumentParser(prog='run_node',
21
32
  formatter_class=argparse.RawDescriptionHelpFormatter,
22
33
  description='Script to run a single node in an SC flowgraph')
23
34
 
35
+ # Define command-line arguments
24
36
  parser.add_argument('-version',
25
37
  action='version',
26
38
  version=__version__)
@@ -75,16 +87,17 @@ def main():
75
87
  help='Running as replay')
76
88
  args = parser.parse_args()
77
89
 
78
- # Change to working directory to allow rel path to be build dir
79
- # this avoids needing to deal with the job hash on the client
80
- # side
90
+ # Change to the specified working directory. This is crucial for remote
91
+ # runners (like Docker) where paths inside the environment are different
92
+ # from the host.
81
93
  os.chdir(os.path.abspath(args.cwd))
82
94
 
83
95
  # Create the Chip object.
84
96
  chip = Chip('<design>')
97
+ # Load the configuration from the manifest provided.
85
98
  chip.read_manifest(args.cfg)
86
99
 
87
- # setup work directory
100
+ # Configure the chip object based on command-line arguments.
88
101
  chip.set('arg', 'step', args.step)
89
102
  chip.set('arg', 'index', args.index)
90
103
  chip.set('option', 'builddir', os.path.abspath(args.builddir))
@@ -95,40 +108,42 @@ def main():
95
108
  if args.remoteid:
96
109
  chip.set('record', 'remoteid', args.remoteid)
97
110
 
111
+ # If running in a container/remote machine, we unset the scheduler to
112
+ # prevent a recursive scheduling loop.
98
113
  if args.unset_scheduler:
99
114
  for _, step, index in chip.get('option', 'scheduler', 'name',
100
115
  field=None).getvalues():
101
116
  chip.unset('option', 'scheduler', 'name', step=step, index=index)
102
117
 
118
+ # Pre-populate the package cache if a map is provided.
103
119
  if args.cachemap:
104
120
  for cachepair in args.cachemap:
105
121
  package, path = cachepair.split(':')
106
122
  Resolver.set_cache(chip, package, path)
107
123
 
108
- # Populate cache
124
+ # Ensure all package caches are populated before running the node.
109
125
  for resolver in chip.get('package', field='schema').get_resolvers().values():
110
126
  resolver()
111
127
 
112
- # Run the task.
128
+ # Instantiate the SchedulerNode for the specified step and index.
113
129
  error = True
130
+ node = SchedulerNode(
131
+ chip,
132
+ args.step,
133
+ args.index,
134
+ replay=args.replay)
114
135
  try:
115
- SchedulerNode(chip,
116
- args.step,
117
- args.index,
118
- replay=args.replay).run()
136
+ # Execute the node's run() method.
137
+ node.run()
119
138
  error = False
120
-
121
139
  finally:
140
+ # Archive results upon completion, regardless of success or failure.
122
141
  if args.archive:
123
- # Archive the results.
124
142
  with tarfile.open(args.archive,
125
143
  mode='w:gz') as tf:
126
- chip._archive_node(tf,
127
- step=args.step,
128
- index=args.index,
129
- include=args.include)
144
+ node.archive(tf, include=args.include)
130
145
 
131
- # Return success/fail flag, in case the caller is interested.
146
+ # Return a non-zero exit code on error.
132
147
  if error:
133
148
  return 1
134
149
  return 0
@@ -136,4 +151,5 @@ def main():
136
151
 
137
152
  ##########################
138
153
  if __name__ == "__main__":
154
+ # This makes the script executable.
139
155
  sys.exit(main())