siliconcompiler 0.34.1__py3-none-any.whl → 0.34.2__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 (46) hide show
  1. siliconcompiler/__init__.py +14 -2
  2. siliconcompiler/_metadata.py +1 -1
  3. siliconcompiler/apps/sc_show.py +1 -1
  4. siliconcompiler/constraints/__init__.py +17 -0
  5. siliconcompiler/constraints/asic_component.py +378 -0
  6. siliconcompiler/constraints/asic_floorplan.py +449 -0
  7. siliconcompiler/constraints/asic_pins.py +489 -0
  8. siliconcompiler/constraints/asic_timing.py +517 -0
  9. siliconcompiler/core.py +3 -3
  10. siliconcompiler/dependencyschema.py +10 -174
  11. siliconcompiler/design.py +235 -118
  12. siliconcompiler/flowgraph.py +27 -14
  13. siliconcompiler/library.py +133 -0
  14. siliconcompiler/metric.py +94 -72
  15. siliconcompiler/metrics/__init__.py +7 -0
  16. siliconcompiler/metrics/asic.py +245 -0
  17. siliconcompiler/metrics/fpga.py +220 -0
  18. siliconcompiler/package/__init__.py +138 -35
  19. siliconcompiler/package/github.py +6 -10
  20. siliconcompiler/packageschema.py +256 -12
  21. siliconcompiler/pathschema.py +226 -0
  22. siliconcompiler/project.py +459 -0
  23. siliconcompiler/scheduler/docker.py +2 -3
  24. siliconcompiler/scheduler/run_node.py +2 -1
  25. siliconcompiler/scheduler/scheduler.py +4 -13
  26. siliconcompiler/scheduler/schedulernode.py +25 -17
  27. siliconcompiler/scheduler/taskscheduler.py +2 -1
  28. siliconcompiler/schema/__init__.py +0 -2
  29. siliconcompiler/schema/baseschema.py +147 -24
  30. siliconcompiler/schema/editableschema.py +14 -6
  31. siliconcompiler/schema/journal.py +23 -15
  32. siliconcompiler/schema/namedschema.py +6 -4
  33. siliconcompiler/schema/parameter.py +34 -19
  34. siliconcompiler/schema/parametertype.py +2 -0
  35. siliconcompiler/schema/parametervalue.py +198 -15
  36. siliconcompiler/schema/schema_cfg.py +18 -14
  37. siliconcompiler/schema_obj.py +5 -3
  38. siliconcompiler/tool.py +199 -10
  39. siliconcompiler/toolscripts/_tools.json +4 -4
  40. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/METADATA +3 -3
  41. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/RECORD +45 -35
  42. siliconcompiler/schema/packageschema.py +0 -101
  43. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/WHEEL +0 -0
  44. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/entry_points.txt +0 -0
  45. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/licenses/LICENSE +0 -0
  46. {siliconcompiler-0.34.1.dist-info → siliconcompiler-0.34.2.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,517 @@
1
+ from typing import Union, Set, List, Tuple
2
+
3
+ from siliconcompiler.schema import BaseSchema, NamedSchema, EditableSchema, Parameter, \
4
+ PerNode, Scope
5
+ from siliconcompiler import DesignSchema
6
+
7
+
8
+ class ASICTimingScenarioSchema(NamedSchema):
9
+ """
10
+ Represents a single timing scenario for ASIC design constraints.
11
+
12
+ This class encapsulates various parameters that define a specific timing
13
+ scenario, such as operating voltage, temperature, library corners, PEX corners,
14
+ operating mode, SDC filesets, and timing checks to be performed.
15
+ """
16
+
17
+ def __init__(self, name: str = None):
18
+ super().__init__()
19
+ self.set_name(name)
20
+
21
+ schema = EditableSchema(self)
22
+ schema.insert(
23
+ 'voltage', 'default',
24
+ Parameter(
25
+ "float",
26
+ pernode=PerNode.OPTIONAL,
27
+ unit='V',
28
+ scope=Scope.GLOBAL,
29
+ shorthelp="Constraint: pin voltage level",
30
+ switch="-constraint_timing_voltage 'scenario pin <float>'",
31
+ example=["api: chip.set('constraint', 'timing', 'worst', 'voltage', 'VDD', '0.9')"],
32
+ help="""Operating voltage applied to a specific pin in the scenario."""))
33
+
34
+ schema.insert(
35
+ 'temperature',
36
+ Parameter(
37
+ 'float',
38
+ pernode=PerNode.OPTIONAL,
39
+ unit='C',
40
+ scope=Scope.GLOBAL,
41
+ shorthelp="Constraint: temperature",
42
+ switch="-constraint_timing_temperature 'scenario <float>'",
43
+ example=["api: chip.set('constraint', 'timing', 'worst', 'temperature', '125')"],
44
+ help="""Chip temperature applied to the scenario specified in degrees C."""))
45
+
46
+ schema.insert(
47
+ 'libcorner',
48
+ Parameter(
49
+ '{str}',
50
+ pernode=PerNode.OPTIONAL,
51
+ scope=Scope.GLOBAL,
52
+ shorthelp="Constraint: library corner",
53
+ switch="-constraint_timing_libcorner 'scenario <str>'",
54
+ example=["api: chip.set('constraint', 'timing', 'worst', 'libcorner', 'ttt')"],
55
+ help="""List of characterization corners used to select
56
+ timing files for all logiclibs and macrolibs."""))
57
+
58
+ schema.insert(
59
+ 'pexcorner',
60
+ Parameter(
61
+ 'str',
62
+ pernode=PerNode.OPTIONAL,
63
+ scope=Scope.GLOBAL,
64
+ shorthelp="Constraint: pex corner",
65
+ switch="-constraint_timing_pexcorner 'scenario <str>'",
66
+ example=["api: chip.set('constraint', 'timing', 'worst', 'pexcorner', 'max')"],
67
+ help="""Parasitic corner applied to the scenario. The
68
+ 'pexcorner' string must match a corner found in :keypath:`pdk,<pdk>,pexmodel`."""))
69
+
70
+ schema.insert(
71
+ 'opcond',
72
+ Parameter(
73
+ 'str',
74
+ pernode=PerNode.OPTIONAL,
75
+ scope=Scope.GLOBAL,
76
+ shorthelp="Constraint: operating condition",
77
+ switch="-constraint_timing_opcond 'scenario <str>'",
78
+ example=["api: chip.set('constraint', 'timing', 'worst', 'opcond', 'typical_1.0')"],
79
+ help="""Operating condition applied to the scenario. The value
80
+ can be used to access specific conditions within the library
81
+ timing models from the :keypath:`asic,logiclib` timing models."""))
82
+
83
+ schema.insert(
84
+ 'mode',
85
+ Parameter(
86
+ 'str',
87
+ pernode=PerNode.OPTIONAL,
88
+ scope=Scope.GLOBAL,
89
+ shorthelp="Constraint: operating mode",
90
+ switch="-constraint_timing_mode 'scenario <str>'",
91
+ example=["api: chip.set('constraint', 'timing', 'worst', 'mode', 'test')"],
92
+ help="""Operating mode for the scenario. Operating mode strings
93
+ can be values such as test, functional, standby."""))
94
+
95
+ schema.insert(
96
+ 'sdcfileset',
97
+ Parameter(
98
+ '[(str,str)]',
99
+ pernode=PerNode.OPTIONAL,
100
+ scope=Scope.GLOBAL,
101
+ shorthelp="Constraint: SDC files",
102
+ switch="-constraint_timing_file 'scenario <file>'",
103
+ example=["api: chip.set('constraint', 'timing', 'worst', 'file', 'hello.sdc')"],
104
+ help="""List of timing constraint sets files to use for the scenario. The
105
+ values are combined with any constraints specified by the design
106
+ 'constraint' parameter. If no constraints are found, a default
107
+ constraint file is used based on the clock definitions."""))
108
+
109
+ schema.insert(
110
+ 'check',
111
+ Parameter(
112
+ '{<setup,hold,maxtran,maxcap,mincap,power,leakagepower,dynamicpower,signalem>}',
113
+ pernode=PerNode.OPTIONAL,
114
+ scope=Scope.GLOBAL,
115
+ shorthelp="Constraint: timing checks",
116
+ switch="-constraint_timing_check 'scenario <str>'",
117
+ example=["api: chip.add('constraint', 'timing', 'worst', 'check', 'setup')"],
118
+ help="""
119
+ List of checks for to perform for the scenario. The checks must
120
+ align with the capabilities of the EDA tools and flow being used.
121
+ Checks generally include objectives like meeting setup and hold goals
122
+ and minimize power. Standard check names include setup, hold, power,
123
+ noise, reliability."""))
124
+
125
+ def set_pin_voltage(self,
126
+ pin: str,
127
+ voltage: float,
128
+ step: str = None, index: Union[str, int] = None):
129
+ """
130
+ Sets the voltage for a specified pin.
131
+
132
+ Args:
133
+ pin (str): The name of the pin.
134
+ voltage (float): The voltage value to set.
135
+ step (str, optional): step name.
136
+ index (str, optional): index name.
137
+ """
138
+ return self.set("voltage", pin, voltage, step=step, index=index)
139
+
140
+ def get_pin_voltage(self,
141
+ pin: str,
142
+ step: str = None, index: Union[str, int] = None) -> float:
143
+ """
144
+ Gets the voltage of a specified pin.
145
+
146
+ Args:
147
+ pin (str): The name of the pin.
148
+ step (str, optional): step name.
149
+ index (str, optional): index name.
150
+
151
+ Returns:
152
+ The voltage of the pin.
153
+
154
+ Raises:
155
+ LookupError: If the specified pin does not have a voltage defined.
156
+ """
157
+ if not self.valid("voltage", pin):
158
+ raise LookupError(f"{pin} does not have voltage")
159
+ return self.get("voltage", pin, step=step, index=index)
160
+
161
+ def add_libcorner(self,
162
+ libcorner: str,
163
+ clobber: bool = False,
164
+ step: str = None, index: Union[str, int] = None):
165
+ """
166
+ Adds a library corner to the design.
167
+
168
+ Args:
169
+ libcorner (str): The name of the library corner to add.
170
+ clobber (bool): If True, existing library corners at the specified step/index will
171
+ be overwritten.
172
+ If False (default), the library corner will be added.
173
+ step (str, optional): step name.
174
+ index (str, optional): index name.
175
+ """
176
+ if clobber:
177
+ return self.set("libcorner", libcorner, step=step, index=index)
178
+ else:
179
+ return self.add("libcorner", libcorner, step=step, index=index)
180
+
181
+ def get_libcorner(self,
182
+ step: str = None, index: Union[str, int] = None) -> Set[str]:
183
+ """
184
+ Gets the set of library corners.
185
+
186
+ Args:
187
+ step (str, optional): step name.
188
+ index (str, optional): index name.
189
+
190
+ Returns:
191
+ A set of library corner names.
192
+ """
193
+ return self.get("libcorner", step=step, index=index)
194
+
195
+ def set_pexcorner(self,
196
+ pexcorner: str,
197
+ step: str = None, index: Union[str, int] = None):
198
+ """
199
+ Sets the parasitic extraction (PEX) corner for the design.
200
+
201
+ Args:
202
+ pexcorner (str): The name of the PEX corner to set.
203
+ step (str, optional): step name.
204
+ index (str, optional): index name.
205
+ """
206
+ return self.set("pexcorner", pexcorner, step=step, index=index)
207
+
208
+ def get_pexcorner(self,
209
+ step: str = None, index: Union[str, int] = None) -> str:
210
+ """
211
+ Gets the parasitic extraction (PEX) corner currently set for the design.
212
+
213
+ Args:
214
+ step (str, optional): step name.
215
+ index (str, optional): index name.
216
+
217
+ Returns:
218
+ The name of the PEX corner.
219
+ """
220
+ return self.get("pexcorner", step=step, index=index)
221
+
222
+ def set_mode(self,
223
+ mode: str,
224
+ step: str = None, index: Union[str, int] = None):
225
+ """
226
+ Sets the operational mode for the design.
227
+
228
+ Args:
229
+ mode (str): The operational mode to set (e.g., "func", "scan").
230
+ step (str, optional): step name.
231
+ index (str, optional): index name.
232
+ """
233
+ return self.set("mode", mode, step=step, index=index)
234
+
235
+ def get_mode(self,
236
+ step: str = None, index: Union[str, int] = None) -> str:
237
+ """
238
+ Gets the operational mode currently set for the design.
239
+
240
+ Args:
241
+ step (str, optional): step name.
242
+ index (str, optional): index name.
243
+
244
+ Returns:
245
+ The name of the operational mode.
246
+ """
247
+ return self.get("mode", step=step, index=index)
248
+
249
+ def set_opcond(self,
250
+ opcond: str,
251
+ step: str = None, index: Union[str, int] = None):
252
+ """
253
+ Sets the operating condition for the design.
254
+
255
+ Args:
256
+ opcond (str): The operating condition to set (e.g., "WC", "BC").
257
+ step (str, optional): step name.
258
+ index (str, optional): index name.
259
+ """
260
+ return self.set("opcond", opcond, step=step, index=index)
261
+
262
+ def get_opcond(self,
263
+ step: str = None, index: Union[str, int] = None) -> str:
264
+ """
265
+ Gets the operating condition currently set for the design.
266
+
267
+ Args:
268
+ step (str, optional): step name.
269
+ index (str, optional): index name.
270
+
271
+ Returns:
272
+ The name of the operating condition.
273
+ """
274
+ return self.get("opcond", step=step, index=index)
275
+
276
+ def set_temperature(self,
277
+ temperature: float,
278
+ step: str = None, index: Union[str, int] = None):
279
+ """
280
+ Sets the temperature for the design.
281
+
282
+ Args:
283
+ temperature (float): The temperature value to set in degrees Celsius.
284
+ step (str, optional): step name.
285
+ index (str, optional): index name.
286
+ """
287
+ return self.set("temperature", temperature, step=step, index=index)
288
+
289
+ def get_temperature(self,
290
+ step: str = None, index: Union[str, int] = None) -> float:
291
+ """
292
+ Gets the temperature currently set for the design.
293
+
294
+ Args:
295
+ step (str, optional): step name.
296
+ index (str, optional): index name.
297
+
298
+ Returns:
299
+ The temperature in degrees Celsius.
300
+ """
301
+ return self.get("temperature", step=step, index=index)
302
+
303
+ def add_sdcfileset(self,
304
+ design: Union[DesignSchema, str],
305
+ fileset: str,
306
+ clobber: bool = False,
307
+ step: str = None, index: Union[str, int] = None):
308
+ """
309
+ Adds an SDC fileset for a given design.
310
+
311
+ Args:
312
+ design (:class:`DesignSchema` or str): The design object or the name of the design to
313
+ associate the fileset with.
314
+ fileset (str): The name of the SDC fileset to add.
315
+ clobber (bool): If True, existing SDC filesets for the design at the specified
316
+ step/index will be overwritten.
317
+ If False (default), the SDC fileset will be added.
318
+ step (str, optional): step name.
319
+ index (str, optional): index name.
320
+
321
+ Raises:
322
+ TypeError: If `design` is not a DesignSchema object or a string, or if `fileset` is not
323
+ a string.
324
+ """
325
+ if isinstance(design, DesignSchema):
326
+ design = design.name()
327
+
328
+ if not isinstance(design, str):
329
+ raise TypeError("design must be a design object or string")
330
+
331
+ if not isinstance(fileset, str):
332
+ raise TypeError("fileset must be a string")
333
+
334
+ if clobber:
335
+ return self.set("sdcfileset", (design, fileset), step=step, index=index)
336
+ else:
337
+ return self.add("sdcfileset", (design, fileset), step=step, index=index)
338
+
339
+ def get_sdcfileset(self,
340
+ step: str = None, index: Union[str, int] = None) -> List[Tuple[str, str]]:
341
+ """
342
+ Gets the list of SDC filesets.
343
+
344
+ Args:
345
+ step (str, optional): step name.
346
+ index (str, optional): index name.
347
+
348
+ Returns:
349
+ A list of tuples, where each tuple contains the design name and the SDC fileset name.
350
+ """
351
+ return self.get("sdcfileset", step=step, index=index)
352
+
353
+ def add_check(self,
354
+ check: str,
355
+ clobber: bool = False,
356
+ step: str = None, index: Union[str, int] = None):
357
+ """
358
+ Adds a check to the design process.
359
+
360
+ Args:
361
+ check (str): The name of the check to add.
362
+ clobber (bool): If True, existing checks at the specified step/index will
363
+ be overwritten.
364
+ If False (default), the check will be added.
365
+ step (str, optional): step name.
366
+ index (str, optional): index name.
367
+ """
368
+ if clobber:
369
+ return self.set("check", check, step=step, index=index)
370
+ else:
371
+ return self.add("check", check, step=step, index=index)
372
+
373
+ def get_check(self, step: str = None, index: Union[str, int] = None) -> Set[str]:
374
+ """
375
+ Gets the set of checks configured for the design process.
376
+
377
+ Args:
378
+ step (str, optional): step name.
379
+ index (str, optional): index name.
380
+
381
+ Returns:
382
+ A set of check names.
383
+ """
384
+ return self.get("check", step=step, index=index)
385
+
386
+
387
+ class ASICTimingConstraintSchema(BaseSchema):
388
+ """
389
+ Manages a collection of ASIC timing scenarios for design constraints.
390
+
391
+ This class provides methods to add, retrieve, create, and remove
392
+ individual :class:`ASICTimingScenarioSchema` objects, allowing for organized
393
+ management of various timing-related constraints for different operating
394
+ conditions or analysis modes.
395
+ """
396
+
397
+ def __init__(self):
398
+ super().__init__()
399
+
400
+ EditableSchema(self).insert("default", ASICTimingScenarioSchema())
401
+
402
+ def add_scenario(self, scenario: ASICTimingScenarioSchema):
403
+ """
404
+ Adds a timing scenario to the design configuration.
405
+
406
+ This method is responsible for incorporating a new or updated timing scenario
407
+ into the system's configuration. If a scenario with the same name already
408
+ exists, it will be overwritten (`clobber=True`).
409
+
410
+ Args:
411
+ scenario: The :class:`ASICTimingScenarioSchema` object representing the timing scenario
412
+ to add. This object must have a valid name defined via its `name()` method.
413
+
414
+ Raises:
415
+ TypeError: If the provided `scenario` argument is not an instance of
416
+ :class:`ASICTimingScenarioSchema`.
417
+ ValueError: If the `scenario` object's `name()` method returns None, indicating
418
+ that the scenario does not have a defined name.
419
+ """
420
+ if not isinstance(scenario, ASICTimingScenarioSchema):
421
+ raise TypeError("scenario must be a timing scenario object")
422
+
423
+ if scenario.name() is None:
424
+ raise ValueError("scenario must have a name")
425
+
426
+ EditableSchema(self).insert(scenario.name(), scenario, clobber=True)
427
+
428
+ def get_scenario(self, scenario: str = None):
429
+ """
430
+ Retrieves one or all timing scenarios from the configuration.
431
+
432
+ This method provides flexibility to fetch either a specific timing scenario
433
+ by its name or a collection of all currently defined scenarios.
434
+
435
+ Args:
436
+ scenario (str, optional): The name (string) of the specific timing scenario to retrieve.
437
+ If this argument is omitted or set to None, the method will return
438
+ a dictionary containing all available timing scenarios.
439
+
440
+ Returns:
441
+ - If `scenario` is provided: The :class:`ASICTimingScenarioSchema` object corresponding
442
+ to the specified scenario name.
443
+ - If `scenario` is None: A dictionary where keys are scenario names (str) and
444
+ values are their respective :class:`ASICTimingScenarioSchema` objects.
445
+
446
+ Raises:
447
+ LookupError: If a specific `scenario` name is provided but no scenario with
448
+ that name is found in the configuration.
449
+ """
450
+ if scenario is None:
451
+ scenarios = {}
452
+ for scenario in self.getkeys():
453
+ scenarios[scenario] = self.get(scenario, field="schema")
454
+ return scenarios
455
+
456
+ if not self.valid(scenario):
457
+ raise LookupError(f"{scenario} is not defined")
458
+ return self.get(scenario, field="schema")
459
+
460
+ def make_scenario(self, scenario: str) -> ASICTimingScenarioSchema:
461
+ """
462
+ Creates and adds a new timing scenario with the specified name.
463
+
464
+ This method initializes a new :class:`ASICTimingScenarioSchema` object with the given
465
+ name and immediately adds it to the constraint configuration. It ensures that
466
+ a scenario with the same name does not already exist, preventing accidental
467
+ overwrites.
468
+
469
+ Args:
470
+ scenario (str): The name for the new timing scenario. This name must be
471
+ a non-empty string and unique within the current configuration.
472
+
473
+ Returns:
474
+ :class:ASICTimingScenarioSchema: The newly created :class:`ASICTimingScenarioSchema`
475
+ object.
476
+
477
+ Raises:
478
+ ValueError: If the provided `scenario` name is empty or None.
479
+ LookupError: If a scenario with the specified `scenario` name already exists
480
+ in the configuration.
481
+ """
482
+ if not scenario:
483
+ raise ValueError("scenario name is required")
484
+
485
+ if self.valid(scenario):
486
+ raise LookupError(f"{scenario} scenario already exists")
487
+
488
+ scenarioobj = ASICTimingScenarioSchema(scenario)
489
+ self.add_scenario(scenarioobj)
490
+ return scenarioobj
491
+
492
+ def remove_scenario(self, scenario: str) -> bool:
493
+ """
494
+ Removes a timing scenario from the design configuration.
495
+
496
+ This method deletes the specified timing scenario from the system's
497
+ configuration.
498
+
499
+ Args:
500
+ scenario (str): The name of the timing scenario to remove.
501
+ This name must be a non-empty string.
502
+
503
+ Returns:
504
+ bool: True if the scenario was successfully removed, False if no
505
+ scenario with the given name was found.
506
+
507
+ Raises:
508
+ ValueError: If the provided `scenario` name is empty or None.
509
+ """
510
+ if not scenario:
511
+ raise ValueError("scenario name is required")
512
+
513
+ if not self.valid(scenario):
514
+ return False
515
+
516
+ EditableSchema(self).remove(scenario)
517
+ return True
siliconcompiler/core.py CHANGED
@@ -37,6 +37,7 @@ from siliconcompiler.utils.flowgraph import _check_flowgraph_io, _get_flowgraph_
37
37
  from siliconcompiler.tools._common import get_tool_task
38
38
  from types import FunctionType, ModuleType
39
39
  from siliconcompiler.flowgraph import RuntimeFlowgraph
40
+ from siliconcompiler.package import Resolver
40
41
 
41
42
 
42
43
  class Chip:
@@ -59,6 +60,8 @@ class Chip:
59
60
  self.scversion = _metadata.version
60
61
  self.schemaversion = SCHEMA_VERSION
61
62
 
63
+ Resolver.reset_cache(self)
64
+
62
65
  # Local variables
63
66
  self.scroot = os.path.dirname(os.path.abspath(__file__))
64
67
  self._error = False
@@ -1478,12 +1481,9 @@ class Chip:
1478
1481
  if keypath[-2:] == ('option', 'builddir'):
1479
1482
  ignore_keys.append(keypath)
1480
1483
 
1481
- package_map = self.get("package", field="schema").get_resolvers()
1482
-
1483
1484
  return self.schema.check_filepaths(
1484
1485
  ignore_keys=ignore_keys,
1485
1486
  logger=self.logger,
1486
- packages=package_map,
1487
1487
  collection_dir=self._getcollectdir(),
1488
1488
  cwd=self.cwd)
1489
1489