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
siliconcompiler/metric.py CHANGED
@@ -1,13 +1,27 @@
1
+ import shutil
2
+ import sys
3
+
4
+ from typing import List, Tuple, TextIO
5
+
1
6
  from siliconcompiler.schema import BaseSchema
2
7
  from siliconcompiler.schema import EditableSchema, Parameter, PerNode, Scope
3
8
  from siliconcompiler.schema.utils import trim
4
9
 
5
- from siliconcompiler.utils.units import convert
10
+ from siliconcompiler.utils import truncate_text, units
6
11
  from siliconcompiler.record import RecordTime
7
12
 
8
13
 
9
14
  class MetricSchema(BaseSchema):
15
+ '''
16
+ Schema for storing and accessing metrics collected during a run.
17
+
18
+ This class provides a structured way to define, record, and report
19
+ various metrics such as runtime, memory usage, and design quality
20
+ indicators for each step of a compilation flow.
21
+ '''
22
+
10
23
  def __init__(self):
24
+ '''Initializes the MetricSchema.'''
11
25
  super().__init__()
12
26
 
13
27
  schema = EditableSchema(self)
@@ -98,25 +112,33 @@ class MetricSchema(BaseSchema):
98
112
 
99
113
  def clear(self, step, index):
100
114
  '''
101
- Clear all saved metrics for a given step and index
115
+ Clears all saved metrics for a given step and index.
102
116
 
103
117
  Args:
104
- step (str): Step name to clear.
105
- index (str/int): Index name to clear.
118
+ step (str): The step name to clear metrics for.
119
+ index (str or int): The index to clear metrics for.
106
120
  '''
107
121
  for metric in self.getkeys():
108
122
  self.unset(metric, step=step, index=str(index))
109
123
 
110
124
  def record(self, step, index, metric, value, unit=None):
111
125
  """
112
- Record a metric
126
+ Records a metric value for a specific step and index.
127
+
128
+ This method handles unit conversion if the metric is defined with a
129
+ unit in the schema.
113
130
 
114
131
  Args:
115
- step (str): step to record
116
- index (str/int): index to record
117
- metric (str): name of metric
118
- value (int/float): value to record
119
- unit (str): unit associated with value
132
+ step (str): The step to record the metric for.
133
+ index (str or int): The index to record the metric for.
134
+ metric (str): The name of the metric to record.
135
+ value (int or float): The value of the metric.
136
+ unit (str, optional): The unit of the provided value. If the schema
137
+ defines a unit for this metric, the value will be converted.
138
+ Defaults to None.
139
+
140
+ Returns:
141
+ The recorded value after any unit conversion.
120
142
  """
121
143
  metric_unit = self.get(metric, field='unit')
122
144
 
@@ -124,18 +146,21 @@ class MetricSchema(BaseSchema):
124
146
  raise ValueError(f"{metric} does not have a unit, but {unit} was supplied")
125
147
 
126
148
  if metric_unit:
127
- value = convert(value, from_unit=unit, to_unit=metric_unit)
149
+ value = units.convert(value, from_unit=unit, to_unit=metric_unit)
128
150
 
129
151
  return self.set(metric, value, step=step, index=str(index))
130
152
 
131
153
  def record_tasktime(self, step, index, record):
132
154
  """
133
- Record the task time for this node
155
+ Records the task time for a given node based on start and end times.
134
156
 
135
157
  Args:
136
- step (str): step to record
137
- index (str/int): index to record
138
- record (:class:`RecordSchema`): record to lookup data in
158
+ step (str): The step of the node.
159
+ index (str or int): The index of the node.
160
+ record (RecordSchema): The record schema containing timing data.
161
+
162
+ Returns:
163
+ bool: True if the time was successfully recorded, False otherwise.
139
164
  """
140
165
  start_time, end_time = [
141
166
  record.get_recorded_time(step, index, RecordTime.START),
@@ -149,13 +174,19 @@ class MetricSchema(BaseSchema):
149
174
 
150
175
  def record_totaltime(self, step, index, flow, record):
151
176
  """
152
- Record the total time for this node
177
+ Records the cumulative total time up to the end of a given node.
178
+
179
+ This method calculates the total wall-clock time by summing the
180
+ durations of all previously executed parallel tasks.
153
181
 
154
182
  Args:
155
- step (str): step to record
156
- index (str/int): index to record
157
- flow (:class:`FlowgraphSchema`): flowgraph to lookup nodes in
158
- record (:class:`RecordSchema`): record to lookup data in
183
+ step (str): The step of the node.
184
+ index (str or int): The index of the node.
185
+ flow (FlowgraphSchema): The flowgraph containing the nodes.
186
+ record (RecordSchema): The record schema containing timing data.
187
+
188
+ Returns:
189
+ bool: True if the time was successfully recorded, False otherwise.
159
190
  """
160
191
  all_nodes = flow.get_nodes()
161
192
  node_times = [
@@ -205,9 +236,182 @@ class MetricSchema(BaseSchema):
205
236
 
206
237
  return self.record(step, index, "totaltime", total_time, unit="s")
207
238
 
239
+ def get_formatted_metric(self, metric: str, step: str, index: str) -> str:
240
+ '''
241
+ Retrieves and formats a metric for display.
242
+
243
+ Handles special formatting for memory (binary units), time, and adds
244
+ SI suffixes for other float values.
245
+
246
+ Args:
247
+ metric (str): The name of the metric to format.
248
+ step (str): The step of the metric.
249
+ index (str): The index of the metric.
250
+
251
+ Returns:
252
+ str: The formatted, human-readable metric value as a string.
253
+ '''
254
+ if metric == 'memory':
255
+ return units.format_binary(self.get(metric, step=step, index=index),
256
+ self.get(metric, field="unit"))
257
+ elif metric in ['exetime', 'tasktime', 'totaltime']:
258
+ return units.format_time(self.get(metric, step=step, index=index))
259
+ elif self.get(metric, field="type") == 'int':
260
+ return str(self.get(metric, step=step, index=index))
261
+ else:
262
+ return units.format_si(self.get(metric, step=step, index=index),
263
+ self.get(metric, field="unit"))
264
+
265
+ def summary_table(self,
266
+ nodes: List[Tuple[str, str]] = None,
267
+ column_width: int = 15,
268
+ formatted: bool = True,
269
+ trim_empty_metrics: bool = True):
270
+ '''
271
+ Generates a summary of metrics as a pandas DataFrame.
272
+
273
+ Args:
274
+ nodes (List[Tuple[str, str]], optional): A list of (step, index)
275
+ tuples to include in the summary. If None, all nodes with
276
+ metrics are included. Defaults to None.
277
+ column_width (int, optional): The width for each column.
278
+ Defaults to 15.
279
+ formatted (bool, optional): If True, metric values are formatted
280
+ for human readability. Defaults to True.
281
+ trim_empty_metrics (bool, optional): If True, metrics that have no
282
+ value for any of the specified nodes are excluded.
283
+ Defaults to True.
284
+
285
+ Returns:
286
+ pandas.DataFrame: A DataFrame containing the metric summary.
287
+ '''
288
+ from pandas import DataFrame
289
+
290
+ if not nodes:
291
+ nodes = set()
292
+ for metric in self.getkeys():
293
+ for value, step, index in self.get(metric, field=None).getvalues():
294
+ nodes.add((step, index))
295
+ nodes = list(sorted(nodes))
296
+
297
+ row_labels = list(self.getkeys())
298
+ sort_map = {metric: 0 for metric in row_labels}
299
+ sort_map["errors"] = -2
300
+ sort_map["warnings"] = -1
301
+ sort_map["memory"] = 1
302
+ sort_map["exetime"] = 2
303
+ sort_map["tasktime"] = 3
304
+ sort_map["totaltime"] = 4
305
+ row_labels = sorted(row_labels, key=lambda row: sort_map[row])
306
+
307
+ if trim_empty_metrics:
308
+ for metric in self.getkeys():
309
+ data = []
310
+ for step, index in nodes:
311
+ data.append(self.get(metric, step=step, index=index))
312
+ if all([dat is None for dat in data]):
313
+ row_labels.remove(metric)
314
+
315
+ if 'totaltime' in row_labels:
316
+ if not any([self.get('totaltime', step=step, index=index) is None
317
+ for step, index in nodes]):
318
+ nodes.sort(
319
+ key=lambda node: self.get('totaltime', step=node[0], index=node[1]))
320
+
321
+ # trim labels to column width
322
+ column_labels = ["unit"]
323
+ labels = [f'{step}/{index}' for step, index in nodes]
324
+ if labels:
325
+ column_width = min([column_width, max([len(label) for label in labels])])
326
+
327
+ for label in labels:
328
+ column_labels.append(truncate_text(label, column_width).center(column_width))
329
+
330
+ if formatted:
331
+ none_value = "---".center(column_width)
332
+ else:
333
+ none_value = None
334
+
335
+ data = []
336
+ for metric in row_labels:
337
+ row = [self.get(metric, field="unit") or ""]
338
+ for step, index in nodes:
339
+ value = self.get(metric, step=step, index=index)
340
+ if value is None:
341
+ value = none_value
342
+ else:
343
+ if formatted:
344
+ value = self.get_formatted_metric(metric, step=step, index=index)
345
+ value = value.center(column_width)
346
+ else:
347
+ value = self.get(metric, step=step, index=index)
348
+ row.append(value)
349
+ data.append(row)
350
+
351
+ return DataFrame(data, row_labels, column_labels)
352
+
353
+ def summary(self,
354
+ headers: List[Tuple[str, str]],
355
+ nodes: List[Tuple[str, str]] = None,
356
+ column_width: int = 15,
357
+ fd: TextIO = None) -> None:
358
+ '''
359
+ Prints a formatted summary of metrics to a file descriptor.
360
+
361
+ Args:
362
+ headers (List[Tuple[str, str]]): A list of (title, value) tuples
363
+ to print in the header section of the summary.
364
+ nodes (List[Tuple[str, str]], optional): A list of (step, index)
365
+ tuples to include. Defaults to all nodes.
366
+ column_width (int, optional): The width for each column in the table.
367
+ Defaults to 15.
368
+ fd (TextIO, optional): The file descriptor to write to.
369
+ Defaults to `sys.stdout`.
370
+ '''
371
+ header = []
372
+ headers.insert(0, ("SUMMARY", None))
373
+ if headers:
374
+ max_header = max([len(title) for title, _ in headers])
375
+ for title, value in headers:
376
+ if value is None:
377
+ header.append(f"{title:<{max_header}} :")
378
+ else:
379
+ header.append(f"{title:<{max_header}} : {value}")
380
+
381
+ max_line_width = max(4 * column_width, int(0.95*shutil.get_terminal_size().columns))
382
+ data = self.summary_table(nodes=nodes, column_width=column_width)
383
+
384
+ if not fd:
385
+ fd = sys.stdout
386
+
387
+ print("-" * max_line_width, file=fd)
388
+ print("\n".join(header), file=fd)
389
+ print(file=fd)
390
+ if data.empty:
391
+ print(" No metrics to display!", file=fd)
392
+ else:
393
+ print(data.to_string(line_width=max_line_width, col_space=3), file=fd)
394
+ print("-" * max_line_width, file=fd)
395
+
396
+ @classmethod
397
+ def _getdict_type(cls) -> str:
398
+ """
399
+ Returns the metadata type for `getdict` serialization.
400
+ """
401
+
402
+ return MetricSchema.__name__
403
+
208
404
 
209
405
  class MetricSchemaTmp(MetricSchema):
406
+ '''
407
+ A temporary schema containing a wide range of common ASIC and FPGA metrics.
408
+
409
+ This class extends the base `MetricSchema` with numerous metrics related to
410
+ design rules, area, timing, power, and physical implementation details.
411
+ '''
412
+
210
413
  def __init__(self):
414
+ '''Initializes the MetricSchemaTmp.'''
211
415
  super().__init__()
212
416
 
213
417
  schema_metric_tmp(self)
@@ -217,6 +421,15 @@ class MetricSchemaTmp(MetricSchema):
217
421
  # Metrics to Track
218
422
  ###########################################################################
219
423
  def schema_metric_tmp(schema):
424
+ '''
425
+ Defines a temporary, extended set of metrics in the provided schema.
426
+
427
+ This function populates a schema with a comprehensive list of metrics
428
+ commonly used in ASIC and FPGA design flows.
429
+
430
+ Args:
431
+ schema (Schema): The schema object to configure.
432
+ '''
220
433
  schema = EditableSchema(schema)
221
434
 
222
435
  metrics = {'drvs': 'design rule violations',